onkyo_eiscp_ruby 2.0.0 → 2.1.5

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
- SHA1:
3
- metadata.gz: c2f2a55fcfb2dac1b649b3efe36065ea7e0e58af
4
- data.tar.gz: 6c0c5f56a5fdaff8c5a3aa6673e9e12cf96bd32a
2
+ SHA256:
3
+ metadata.gz: d35b01beb072ac7058ef54920b734aff8d4d098ecc702eb9edd21ee818ad7ca0
4
+ data.tar.gz: f1112ac834d03f1dea2e50aeed301ac8f0e802b55b00b0f317d7802515ffa84e
5
5
  SHA512:
6
- metadata.gz: 2ee13a3c0b65c4b2b57872a8656ba59e07e9f1f7aaa0f4a7af90a55512f6db50d435333100b8e5ac86ab6adc2e62438649d54639810bb9be231c2275f0524caf
7
- data.tar.gz: 7d0ea01812008c6ad24525d358684b84dabe14f8192dfe0f400fb24727fa1988e89374e4c24402ac132b92f723bc67de0a1c0923c27694f5db3571ddb4e63083
6
+ metadata.gz: f3a50446a0df21efd8dc6b2397daeb0cf791a997e7f0c27bb9d01c98b9e08fdc99cd68176b464882b29acc64a5f9cbe0748da690da51269f10f8f41f3d75dfc7
7
+ data.tar.gz: 65bafa48d392b8578435f006de9b27b8e58b9f16d7ccd7e2d527b38f23c0e2db187204c79f9c2dfe121021907735b51f73b82cef8fb82789ac362b5c3fec228f
data/README.md CHANGED
@@ -3,19 +3,24 @@ onkyo_eiscp_ruby
3
3
  [![Gem Version](https://badge.fury.io/rb/onkyo_eiscp_ruby.png)](http://badge.fury.io/rb/onkyo_eiscp_ruby)
4
4
  [![GitHub version](https://badge.fury.io/gh/mikerodrigues%2Fonkyo_eiscp_ruby.png)](http://badge.fury.io/gh/mikerodrigues%2Fonkyo_eiscp_ruby)
5
5
 
6
- *A Ruby implementation of eISCP for controlling Onkyo receivers.*
6
+ *A Ruby implementation of eISCP (ethernet Integra Serial Control Protocol) for controlling Onkyo receivers.*
7
7
 
8
- **This code is still under heavy development and using it might make you sick.**
8
+ **I'm still sort of updating this code. Please feel free to reach out if there's something you need that it doesn't do, I may be willing to help.**
9
9
 
10
- Automatically discover receivers in the broadcast domain
10
+ **The python version linked below sees much more activity.**
11
11
 
12
- Send commands to receivers and parse returned messages
13
12
 
14
- Open a TCP socket to receive solicited and non-solicited status updates.
13
+ Features
14
+ ---------------
15
+ * Automatically discover receivers in the broadcast domain
16
+
17
+ * Send commands to receivers and parse returned messages
18
+
19
+ * Open a TCP socket to receive solicited and non-solicited status updates.
15
20
 
16
- Mock reciever (currently only responds to discovery)
21
+ * Mock reciever (currently only responds to discovery)
17
22
 
18
- Human-readable commands
23
+ * Human-readable commands
19
24
 
20
25
  **Inspired by https://github.com/miracle2k/onkyo-eiscp
21
26
 
@@ -38,7 +43,11 @@ What's missing?
38
43
 
39
44
  Using the Library
40
45
  -----------------
41
- * require the library
46
+ * Install the library
47
+
48
+ gem install onkyo_eiscp_ruby
49
+
50
+ * Require the library
42
51
 
43
52
  require 'eiscp'
44
53
 
@@ -160,6 +169,11 @@ Using the Library
160
169
 
161
170
  # Set the master volume to 45
162
171
  receiver.master_volume "45"
172
+
173
+ # Change the input to TV/CD
174
+ # Note: when a command value has more than one name (an array in the YAML file)
175
+ # we default to using the first entry. So for `['cd', 'tv', 'cd']` you get:
176
+ receiver.input_selector "cd"
163
177
  ```
164
178
 
165
179
  * Parse ISCP and human readable strings:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.1.5
data/bin/mock_receiver.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
2
4
  require_relative './receiver'
3
5
  require_relative './message'
@@ -9,7 +11,8 @@ module EISCP
9
11
  #
10
12
  class MockReceiver
11
13
  DISCOVERY_IP = '255.255.255.255'
12
- ONKYO_DISCOVERY_RESPONSE = Message.new('ECN', "TX-NR609/60128/DX/14DAE9E967C8\x19\r\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
14
+ ONKYO_DISCOVERY_RESPONSE = Message.new('ECN',
15
+ "TX-NR609/60128/DX/14DAE9E967C8\x19\r\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
13
16
 
14
17
  # Create/start the server object.
15
18
 
data/bin/onkyo.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'eiscp'
4
5
  require 'optparse'
@@ -7,14 +8,13 @@ require 'ostruct'
7
8
  # This object parses ARGV and returns an @option hash
8
9
  #
9
10
  class Options
10
- DEFAULT_OPTIONS = { verbose: true, all: false }
11
+ DEFAULT_OPTIONS = { verbose: true, all: false }.freeze
11
12
  USAGE = ' Usage: onkyo_rb [options]'
12
13
 
13
14
  def self.parse(args)
14
15
  @options = OpenStruct.new
15
16
 
16
17
  options = OptionParser.new do |opts|
17
-
18
18
  opts.banner = USAGE
19
19
 
20
20
  opts.on '-d', '--discover', 'Find Onkyo Receivers on the local broadcast domain' do |d|
@@ -38,17 +38,16 @@ class Options
38
38
  end
39
39
 
40
40
  opts.on '-m', '--monitor', 'Connect to the first discovered reciever and monitor updates' do |m|
41
- @options.monitor = m
41
+ @options.monitor = m
42
42
  end
43
-
44
43
  end
45
44
 
46
45
  options.parse!(args)
47
46
 
48
- if @options.nil? && ARGV == [] then puts options end
47
+ puts options if @options.nil? && ARGV == []
49
48
 
50
49
  if @options.discover
51
- EISCP::Receiver.discover.each {|rec| puts "#{rec.host}:#{rec.port} - #{rec.model} - #{rec.mac_address}"}
50
+ EISCP::Receiver.discover.each { |rec| puts "#{rec.host}:#{rec.port} - #{rec.model} - #{rec.mac_address}" }
52
51
  exit 0
53
52
  end
54
53
 
@@ -59,7 +58,7 @@ class Options
59
58
 
60
59
  if @options.monitor
61
60
  begin
62
- rec = EISCP::Receiver.new do |reply|
61
+ rec = EISCP::Receiver.new do |reply|
63
62
  puts "#{Time.now} #{rec.host} "\
64
63
  "#{reply.zone}: "\
65
64
  "#{reply.command_description || reply.command} "\
@@ -67,9 +66,9 @@ class Options
67
66
  end
68
67
  rec.thread.join
69
68
  rescue Interrupt
70
- fail 'Exiting...'
69
+ raise 'Exiting...'
71
70
  rescue Exception => e
72
- puts "bummer..."
71
+ puts 'bummer...'
73
72
  puts e
74
73
  end
75
74
  end
@@ -79,28 +78,25 @@ class Options
79
78
  modelsets = []
80
79
  EISCP::Receiver.discover.each do |rec|
81
80
  models << rec.model
82
- end
81
+ end
83
82
  models.each do |model|
84
83
  EISCP::Dictionary.modelsets.each do |modelset, list|
85
- if list.select{|x| x.match model}.length > 0
86
- modelsets << modelset
87
- end
84
+ modelsets << modelset unless list.select { |x| x.match model }.empty?
88
85
  end
89
86
  end
90
87
  EISCP::Dictionary.zones.each do |zone|
91
88
  EISCP::Dictionary.commands[zone].each do |command, command_hash|
92
- puts "Command - Description"
89
+ puts 'Command - Description'
93
90
  puts "\n"
94
91
  puts " '#{Dictionary.command_to_name(command)}' - "\
95
92
  "#{Dictionary.description_from_command(command)}"
96
93
  puts "\n"
97
- puts " Value - Description>"
94
+ puts ' Value - Description>'
98
95
  puts "\n"
99
- command_hash[:values].each do |value, attr_hash|
100
- if modelsets.include? attr_hash[:models]
101
- puts " '#{attr_hash[:name]}' - "\
102
- " #{attr_hash[:description]}"
103
- else
96
+ command_hash[:values].each do |_value, attr_hash|
97
+ if modelsets.include? attr_hash[:models]
98
+ puts " '#{EISCP::Dictionary.command_value_to_value_name(command, _value)}' - "\
99
+ " #{attr_hash[:description]}"
104
100
  end
105
101
  end
106
102
  puts "\n"
@@ -111,16 +107,16 @@ class Options
111
107
  if @options.list_all
112
108
  EISCP::Dictionary.zones.each do |zone|
113
109
  EISCP::Dictionary.commands[zone].each do |command, command_hash|
114
- puts "Command - Description"
110
+ puts 'Command - Description'
115
111
  puts "\n"
116
112
  puts " '#{Dictionary.command_to_name(command)}' - "\
117
113
  "#{Dictionary.description_from_command(command)}"
118
114
  puts "\n"
119
- puts " Value - Description>"
115
+ puts ' Value - Description>'
120
116
  puts "\n"
121
- command_hash[:values].each do |value, attr_hash|
122
- puts " '#{attr_hash[:name]}' - "\
123
- " #{attr_hash[:description]}"
117
+ command_hash[:values].each do |_value, attr_hash|
118
+ puts " '#{EISCP::Dictionary.command_value_to_value_name(command, _value)}' - "\
119
+ " #{attr_hash[:description]}"
124
120
  end
125
121
  puts "\n"
126
122
  end
@@ -143,7 +139,7 @@ receiver = EISCP::Receiver.discover[0]
143
139
  receiver.connect
144
140
  begin
145
141
  command = EISCP::Parser.parse(ARGV.join(' '))
146
- rescue
142
+ rescue StandardError
147
143
  raise "Couldn't parse command"
148
144
  end
149
145
  reply = receiver.send_recv(command)
data/bin/onkyo_server.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'eiscp/mock_receiver'
4
5
 
data/lib/eiscp.rb CHANGED
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Library for controlling Onkyo receivers over TCP/IP.
2
4
  #
3
5
  module EISCP
4
- VERSION = '2.0.0'
6
+ VERSION = '2.1.5'
5
7
  end
6
8
 
7
9
  require_relative './eiscp/receiver'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative './dictionary/dictionary_generators'
2
4
  require_relative './dictionary/dictionary_helpers'
3
5
 
@@ -12,14 +14,12 @@ module EISCP
12
14
  extend DictionaryHelpers
13
15
 
14
16
  class << self
15
- attr_reader :zones
16
- attr_reader :modelsets
17
- attr_reader :commands
17
+ attr_reader :zones, :modelsets, :commands
18
18
  end
19
19
 
20
20
  DEFAULT_ZONE = 'main'
21
- @yaml_file_path = File.join(File.expand_path(File.dirname(__FILE__)), '../../eiscp-commands.yaml')
22
- @commands = YAML.load(File.read(@yaml_file_path))
21
+ @yaml_file_path = File.join(__dir__, '../../eiscp-commands.yaml')
22
+ @commands = YAML.safe_load(File.read(@yaml_file_path), permitted_classes: [Symbol])
23
23
  @modelsets = @commands[:modelsets]
24
24
  @commands.delete(:modelsets)
25
25
  @zones = @commands.map { |k, _| k }
@@ -30,11 +30,12 @@ module EISCP
30
30
  command = command[0]
31
31
  @commands[zone][command][:values].each do |value|
32
32
  value = value[0]
33
- if value.is_a? Array
33
+ case value
34
+ when Array
34
35
  @additions << [zone, command, value, create_range_commands(zone, command, value)]
35
- elsif value.match(/^(B|T){xx}$/)
36
+ when /^(B|T){xx}$/
36
37
  @additions << [zone, command, value, create_treble_bass_commands(zone, command, value)]
37
- elsif value.match(/^{xx}$/)
38
+ when /^{xx}$/
38
39
  @additions << [zone, command, value, create_balance_commands(zone, command, value)]
39
40
  else
40
41
  next
@@ -44,11 +45,9 @@ module EISCP
44
45
  end
45
46
 
46
47
  @additions.each do |zone, command, value, hash|
47
- begin
48
- @commands[zone][command][:values].merge! hash
49
- rescue
50
- puts "Failed to add #{hash} to #{zone}:#{command}:#{value}"
51
- end
48
+ @commands[zone][command][:values].merge! hash
49
+ rescue StandardError
50
+ puts "Failed to add #{hash} to #{zone}:#{command}:#{value}"
52
51
  end
53
52
  end
54
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  module EISCP
@@ -19,14 +21,12 @@ module EISCP
19
21
  range.each do |number|
20
22
  tmp.merge!(number.to_s(16).rjust(2, '0').upcase =>
21
23
  {
22
- :name => number.to_s,
23
- :description =>
24
- @commands[zone][command][:values][value][:description].gsub(/\d - \d+/, number.to_s),
25
- :models => @commands[zone][command][:values][value][:models]
26
- }
27
- )
24
+ name: number.to_s,
25
+ description: @commands[zone][command][:values][value][:description].gsub(/\d - \d+/, number.to_s),
26
+ models: @commands[zone][command][:values][value][:models]
27
+ })
28
28
  end
29
- return tmp
29
+ tmp
30
30
  end
31
31
 
32
32
  # Creates hash object for treble and bass commands
@@ -36,14 +36,12 @@ module EISCP
36
36
  ['-A', '-8', '-6', '-4', '-2', '00', '+2', '+4', '+6', '+8', '+A'].each do |v|
37
37
  tmp.merge!((value[0] + v.to_s) =>
38
38
  {
39
- :name => value[0].downcase + v,
40
- :description =>
41
- @commands[zone][command][:values][value[0] + '{xx}'][:description].gsub(/\(.*[\]|\)]$/, v),
42
- :models => @commands[zone][command][:values][value[0] + '{xx}'][:models]
43
- }
44
- )
39
+ name: value[0].downcase + v,
40
+ description: @commands[zone][command][:values]["#{value[0]}{xx}"][:description].gsub(/\(.*[\]|)]$/, v),
41
+ models: @commands[zone][command][:values]["#{value[0]}{xx}"][:models]
42
+ })
45
43
  end
46
- return tmp
44
+ tmp
47
45
  end
48
46
 
49
47
  # Creates hash object for balance commands
@@ -53,14 +51,12 @@ module EISCP
53
51
  ['-A', '-8', '-6', '-4', '-2', '00', '+2', '+4', '+6', '+8', '+A'].each do |v|
54
52
  tmp.merge!(v.to_s =>
55
53
  {
56
- :name => v.downcase,
57
- :description =>
58
- @commands[zone][command][:values]['{xx}'][:description].gsub(/\(.*[\]|\)]$/, v),
59
- :models => @commands[zone][command][:values]['{xx}'][:models]
60
- }
61
- )
54
+ name: v.downcase,
55
+ description: @commands[zone][command][:values]['{xx}'][:description].gsub(/\(.*[\]|)]$/, v),
56
+ models: @commands[zone][command][:values]['{xx}'][:models]
57
+ })
62
58
  end
63
- return tmp
59
+ tmp
64
60
  end
65
61
  end
66
62
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module EISCP
2
4
  module Dictionary
3
5
  # This module provides methods to get information from the Dictionary about
@@ -19,9 +21,9 @@ module EISCP
19
21
  command = command.upcase
20
22
  begin
21
23
  zone = zone_from_command(command)
22
- return @commands[zone][command][:name]
23
- rescue
24
- return nil
24
+ @commands[zone][command][:name]
25
+ rescue StandardError
26
+ nil
25
27
  end
26
28
  end
27
29
 
@@ -34,46 +36,50 @@ module EISCP
34
36
  return command if attrs[:name] == name
35
37
  end
36
38
  end
37
- return nil
39
+ nil
38
40
 
39
41
  else
40
42
 
41
43
  @commands[command_zone].each_pair do |command, attrs|
42
44
  return command if attrs[:name] == name
43
45
  end
44
- return nil
46
+ nil
45
47
 
46
48
  end
47
49
  end
48
50
 
49
51
  # Return a value name from a command and a value
50
52
  def command_value_to_value_name(command, value)
51
- begin
52
- zone = zone_from_command(command)
53
- @commands[zone][command][:values][value][:name]
54
- rescue
55
- nil
53
+ zone = zone_from_command(command)
54
+ command_value = @commands[zone][command][:values][value][:name]
55
+ if command_value.instance_of?(String)
56
+ command_value
57
+ elsif command_value.instance_of?(Array)
58
+ command_value.first
56
59
  end
60
+ rescue StandardError
61
+ nil
57
62
  end
58
63
 
59
64
  # Return a value from a command and value name
60
65
  def command_value_name_to_value(command, value_name)
61
- begin
62
- zone = zone_from_command(command)
63
- @commands[zone][command][:values].each_pair do |k, v|
66
+ zone = zone_from_command(command)
67
+ @commands[zone][command][:values].each_pair do |k, v|
68
+ if v[:name].instance_of?(String)
64
69
  return k if v[:name] == value_name.to_s
70
+ elsif v[:name].instance_of?(Array)
71
+ return k if v[:name].first == value_name.to_s
65
72
  end
66
- rescue
67
- nil
68
73
  end
74
+ nil
75
+ rescue StandardError
76
+ nil
69
77
  end
70
78
 
71
79
  # Return a command description from a command name and zone
72
80
  def description_from_command_name(name, zone)
73
81
  @commands[zone].each_pair do |command, attrs|
74
- if attrs[:name] == name
75
- return @commands[zone][command][:description]
76
- end
82
+ return @commands[zone][command][:description] if attrs[:name] == name
77
83
  end
78
84
  nil
79
85
  end
@@ -105,14 +111,11 @@ module EISCP
105
111
  # Checks to see if the command is in the Dictionary
106
112
  #
107
113
  def known_command?(command)
108
- begin
109
- zone = zone_from_command(command)
110
- @commands[zone].include? command
111
- rescue
112
- return nil
113
- end
114
+ zone = zone_from_command(command)
115
+ @commands[zone].include? command
116
+ rescue StandardError
117
+ nil
114
118
  end
115
119
  end
116
-
117
120
  end
118
121
  end
data/lib/eiscp/message.rb CHANGED
@@ -1,4 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  require_relative './dictionary'
3
4
  require_relative './parser'
4
5
 
@@ -14,6 +15,7 @@ module EISCP
14
15
  class Message
15
16
  # EISCP header
16
17
  attr_accessor :header
18
+
17
19
  # ISCP "magic" indicates the start of an eISCP message.
18
20
  MAGIC = 'ISCP'
19
21
  # eISCP header size, fixed length.
@@ -52,41 +54,38 @@ module EISCP
52
54
  # @param [String] value variable length ISCP command value
53
55
  # @param [String] unit_type_character override default unit type character, optional
54
56
  # @param [String] start_character override default start character, optional
55
- def initialize(command: nil, value: nil, terminator: "\r\n", unit_type: '1', start: '!')
57
+ def initialize(command: nil, value: '', terminator: "\r\n", unit_type: '1', start: '!')
56
58
  unless Dictionary.known_command?(command)
57
- #STDERR.puts "Unknown command #{command}"
59
+ # STDERR.puts "Unknown command #{command}"
58
60
  end
59
61
 
60
- fail 'No value specified.' if value.nil?
61
-
62
62
  @command = command
63
63
  @value = value
64
64
  @terminator = terminator
65
65
  @unit_type = unit_type
66
66
  @start = start
67
67
  @header = { magic: MAGIC,
68
- header_size: HEADER_SIZE,
68
+ header_size: HEADER_SIZE,
69
69
  data_size: to_iscp.length,
70
70
  version: ISCP_VERSION,
71
- reserved: RESERVED
72
- }
71
+ reserved: RESERVED }
73
72
  begin
74
73
  get_human_readable_attrs
75
- rescue
76
- #STDERR.puts"Couldn't get all human readable attrs"
74
+ rescue StandardError
75
+ # STDERR.puts"Couldn't get all human readable attrs"
77
76
  end
78
77
  end
79
78
 
80
79
  # Check if two messages are equivalent comparing their ISCP messages.
81
80
  #
82
81
  def ==(other)
83
- to_iscp == other.to_iscp ? true : false
82
+ to_iscp == other.to_iscp
84
83
  end
85
84
 
86
85
  # Return ISCP Message string
87
86
  #
88
87
  def to_iscp
89
- "#{@start + @unit_type + @command + @value}"
88
+ (@start + @unit_type + @command + @value).to_s
90
89
  end
91
90
 
92
91
  # Return EISCP Message string
data/lib/eiscp/parser.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative './parser/eiscp_parser'
2
4
  require_relative './parser/iscp_parser'
3
5
  require_relative './parser/human_readable_parser'
@@ -17,6 +19,6 @@ module EISCP
17
19
  else
18
20
  HumanReadableParser.parse(string)
19
21
  end
20
- end
22
+ end
21
23
  end
22
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module EISCP
2
4
  module Parser
3
5
  module DynamicValueParser
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative './iscp_parser'
2
4
 
3
5
  module EISCP
@@ -15,22 +17,26 @@ module EISCP
15
17
  unit_type: msg.unit_type,
16
18
  start: msg.start
17
19
  )
18
- packet.header = {
20
+ packet.header = create_header(array)
21
+ packet
22
+ end
23
+
24
+ def self.create_header(array)
25
+ {
19
26
  magic: array[0],
20
27
  header_size: array[1],
21
28
  data_size: array[2],
22
29
  version: array[3],
23
30
  reserved: array[4]
24
31
  }
25
- packet
26
32
  end
27
33
 
28
34
  def self.validate(packet)
29
35
  packet.header.header_size.size == packet.command.size
30
36
  end
37
+
31
38
  end
32
39
 
33
- class EISCPParserException < Exception; end
34
-
40
+ class EISCPParserException < RuntimeError; end
35
41
  end
36
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../dictionary'
2
4
 
3
5
  module EISCP
@@ -8,22 +10,20 @@ module EISCP
8
10
  def self.parse(string)
9
11
  array = string.split(' ')
10
12
 
11
- if Dictionary.zones.include? array[0]
12
- parsed_zone = array.shift
13
- else
14
- parsed_zone = Dictionary::DEFAULT_ZONE
15
- end
13
+ parsed_zone = if Dictionary.zones.include? array[0]
14
+ array.shift
15
+ else
16
+ Dictionary::DEFAULT_ZONE
17
+ end
16
18
 
17
19
  command_name = array.shift
18
- value_name = array.join(" ")
20
+ value_name = array.join(' ')
19
21
  command = Dictionary.command_name_to_command(command_name, parsed_zone)
20
22
  value = Dictionary.command_value_name_to_value(command, value_name)
21
- if (command.nil? || value.nil?)
22
- return nil
23
- end
23
+ return nil if command.nil? || value.nil?
24
+
24
25
  Message.new(command: command, value: value)
25
26
  end
26
-
27
27
  end
28
28
  end
29
29
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module EISCP
2
4
  module Parser
3
5
  # This module parses an ISCP string and returns a Message object
4
6
  #
5
7
  module ISCPParser
6
8
  # Regexp for parsing ISCP messages
7
- REGEX = /(?<start>!)?(?<unit_type>(\d|x))?(?<command>[A-Z]{3})\s?(?<value>.*?)(?<terminator>[[:cntrl:]]*$)/
9
+ REGEX = /(?<start>!)?(?<unit_type>(?:\d|x))?(?<command>[A-Z]{3})\s?(?<value>.*?)(?<terminator>[[:cntrl:]]*$)/
8
10
  def self.parse(string)
9
11
  match = string.match(REGEX)
10
12
 
@@ -15,10 +17,7 @@ module EISCP
15
17
  hash.delete_if { |_, v| v.nil? || v == '' }
16
18
 
17
19
  # Convert keys to symbols
18
- hash = hash.inject({}) do |memo, (k, v)|
19
- memo[k.to_sym] = v
20
- memo
21
- end
20
+ hash = hash.transform_keys(&:to_sym)
22
21
 
23
22
  Message.new(**hash)
24
23
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'resolv'
2
4
  require_relative './receiver/discovery'
3
5
  require_relative './receiver/command_methods'
@@ -40,7 +42,6 @@ module EISCP
40
42
  # Default Onkyo eISCP port
41
43
  ONKYO_PORT = 60_128
42
44
 
43
-
44
45
  # Create a new EISCP::Receiver object to communicate with a receiver.
45
46
  # If no host is given, use auto discovery and create a
46
47
  # receiver object using the first host to respond.
@@ -53,7 +54,7 @@ module EISCP
53
54
  # with the Message object that results from a CommandMethod being called.
54
55
  # All we're doing here is calling #send_recv
55
56
  #
56
- command_method_proc = Proc.new {|msg| self.send_recv msg}
57
+ command_method_proc = proc { |msg| send_recv msg }
57
58
  CommandMethods.generate(&command_method_proc)
58
59
 
59
60
  # This proc sets the four ECN attributes and initiates a connection to the
@@ -64,9 +65,7 @@ module EISCP
64
65
  @port = hash[:port]
65
66
  @area = hash[:area]
66
67
  @mac_address = hash[:mac_address]
67
- if block_given?
68
- connect(&block)
69
- end
68
+ connect(&block) if block_given?
70
69
  end
71
70
 
72
71
  # This lambda sets the host IP after resolving it
@@ -83,12 +82,11 @@ module EISCP
83
82
  # Else, use the given host and hash to create a new Receiver object.
84
83
  # This is how ::discover creates Receivers.
85
84
  #
86
- case
87
- when host.nil?
85
+ if host.nil?
88
86
  first_found = Receiver.discover[0]
89
87
  set_host.call first_found.host
90
88
  set_attrs.call first_found.ecn_hash
91
- when info_hash.empty?
89
+ elsif info_hash.empty?
92
90
  set_host.call host
93
91
  Receiver.discover.each do |receiver|
94
92
  receiver.host == @host && set_attrs.call(receiver.ecn_hash)
@@ -99,7 +97,7 @@ module EISCP
99
97
  end
100
98
  end
101
99
 
102
- # Manages the thread and uses the same block passed to throgh #connect.
100
+ # Manages the thread and uses the same block passed to through #connect.
103
101
  #
104
102
  def update_thread
105
103
  # Kill thread if it exists
@@ -114,16 +112,14 @@ module EISCP
114
112
  end
115
113
  private :update_thread
116
114
 
117
- # This creates a socket conection to the receiver if one doesn't exist,
115
+ # This creates a socket conection to the receiver if one doesn't exist,
118
116
  # and updates or sets the callback block if one is passed.
119
117
  #
120
118
  def connect(&block)
121
- begin
122
- @socket ||= TCPSocket.new(@host, @port)
123
- update_thread(&block)
124
- rescue => e
125
- puts e
126
- end
119
+ @socket ||= TCPSocket.new(@host, @port)
120
+ update_thread(&block)
121
+ rescue StandardError => e
122
+ puts e
127
123
  end
128
124
 
129
125
  # Disconnect from the receiver by closing the socket and killing the
@@ -137,9 +133,11 @@ module EISCP
137
133
  # Sends an EISCP::Message object or string on the network
138
134
  #
139
135
  def send(eiscp)
140
- if eiscp.is_a? EISCP::Message
136
+ connect if @socket.nil? || @socket.closed?
137
+ case eiscp
138
+ when EISCP::Message
141
139
  @socket.puts(eiscp.to_eiscp)
142
- elsif eiscp.is_a? String
140
+ when String
143
141
  @socket.puts eiscp
144
142
  end
145
143
  end
@@ -147,18 +145,15 @@ module EISCP
147
145
  # Reads the socket and returns and EISCP::Message
148
146
  #
149
147
  def recv
150
- data = ''
148
+ data = String.new
151
149
  data << @socket.gets until data.match(/\r\n$/)
152
- message = Parser.parse(data)
153
- message
150
+ Parser.parse(data)
154
151
  end
155
152
 
156
153
  # Sends an EISCP::Message object or string on the network and returns recieved data string.
157
154
  #
158
155
  def send_recv(eiscp)
159
- if eiscp.is_a? String
160
- eiscp = Parser.parse(eiscp)
161
- end
156
+ eiscp = Parser.parse(eiscp) if eiscp.is_a? String
162
157
  send eiscp
163
158
  sleep DEFAULT_TIMEOUT
164
159
  Parser.parse("#{eiscp.command}#{@state[eiscp.command]}")
@@ -170,16 +165,15 @@ module EISCP
170
165
  { model: @model,
171
166
  port: @port,
172
167
  area: @area,
173
- mac_address: @mac_address
174
- }
168
+ mac_address: @mac_address }
175
169
  end
176
170
 
177
- # This will return a human-readable represantion of the receiver's state.
171
+ # This will return a human-readable represantion of the receiver's state.
178
172
  #
179
173
  def human_readable_state
180
174
  hash = {}
181
- @state.each do |c, v|
182
- hash["#{Dictionary.command_to_name(c)}"] = "#{Dictionary.command_value_to_value_name(c, v) || v.to_s}"
175
+ @state.dup.each do |c, v|
176
+ hash[Dictionary.command_to_name(c).to_s] = (Dictionary.command_value_to_value_name(c, v) || v.to_s).to_s
183
177
  end
184
178
  hash
185
179
  end
@@ -189,18 +183,12 @@ module EISCP
189
183
  #
190
184
  def update_state
191
185
  Thread.new do
192
- Dictionary.commands.each do |zone, commands|
186
+ Dictionary.commands.each do |zone, _commands|
193
187
  Dictionary.commands[zone].each do |command, info|
194
188
  info[:values].each do |value, _|
195
- if value == 'QSTN'
196
- send(Parser.parse(command + "QSTN"))
197
- # If we send any faster we risk making the stereo drop replies.
198
- # A dropped reply is not necessarily indicative of the
199
- # receiver's failure to receive the command and change state
200
- # accordingly. In this case, we're only making queries, so we do
201
- # want to capture every reply.
202
- sleep DEFAULT_TIMEOUT
203
- end
189
+ next unless value == 'QSTN'
190
+
191
+ send_recv(Parser.parse("#{command}QSTN"))
204
192
  end
205
193
  end
206
194
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../parser'
2
4
  require_relative '../dictionary'
3
5
 
@@ -12,14 +14,12 @@ module EISCP
12
14
  def self.generate(&block)
13
15
  Dictionary.zones.each do |zone|
14
16
  Dictionary.commands[zone].each do |command, _values|
15
- begin
16
- command_name = Dictionary.command_to_name(command).to_s.gsub(/-/, '_')
17
- define_method(command_name) do |v|
18
- self.instance_exec Parser.parse(command_name.gsub(/_/, '-') + ' ' + v), &block
19
- end
20
- rescue => e
21
- puts e
17
+ command_name = Dictionary.command_to_name(command).to_s.gsub(/-/, '_')
18
+ define_method(command_name) do |v|
19
+ instance_exec Parser.parse("#{command_name.gsub(/_/, '-')} #{v}"), &block
22
20
  end
21
+ rescue StandardError => e
22
+ puts e
23
23
  end
24
24
  end
25
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
2
4
  require_relative '../message'
3
5
  require_relative '../parser'
@@ -31,17 +33,14 @@ module EISCP
31
33
  sock.send(ONKYO_MAGIC, 0, '<broadcast>', discovery_port)
32
34
  data = []
33
35
  loop do
34
-
35
- begin
36
- msg, addr = sock.recvfrom_nonblock(1024)
37
- data << Receiver.new(addr[2], ecn_string_to_ecn_array(msg))
38
- rescue IO::WaitReadable
39
- io = IO.select([sock], nil, nil, 0.5)
40
- if io.nil?
41
- return data
42
- else
43
- retry
44
- end
36
+ msg, addr = sock.recvfrom_nonblock(1024)
37
+ data << Receiver.new(addr[2], ecn_string_to_ecn_array(msg))
38
+ rescue IO::WaitReadable
39
+ io = IO.select([sock], nil, nil, 0.5)
40
+ if io.nil?
41
+ return data
42
+ else
43
+ retry
45
44
  end
46
45
  end
47
46
  end
@@ -1,28 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Gem::Specification.new do |s|
2
4
  s.name = 'onkyo_eiscp_ruby'
3
5
  s.version = File.read(File.expand_path('VERSION', File.dirname(__FILE__))).strip
4
- s.licenses = ['MIT']
6
+ s.licenses = ['MIT']
5
7
  s.platform = Gem::Platform::RUBY
6
8
  s.summary = 'Manipulate Onkyo stereos with the eISCP protocol'
7
9
  s.files = Dir.glob('{bin,config,lib,test,doc}/**/*') +
8
- ['VERSION', 'onkyo_eiscp_ruby.gemspec', 'eiscp-commands.yaml']
10
+ ['VERSION', 'onkyo_eiscp_ruby.gemspec', 'eiscp-commands.yaml']
9
11
  s.extra_rdoc_files = ['README.md']
10
12
  s.require_path = 'lib'
11
13
 
12
14
  s.homepage = 'https://github.com/mikerodrigues/onkyo_eiscp_ruby'
13
15
 
14
- s.description = %q(
16
+ s.description = '
15
17
  Control Onkyo receivers over the network.Use the provided binary or
16
18
  require the library for use in your scripts.
17
- )
19
+ '
18
20
 
19
21
  s.author = 'Michael Rodrigues'
20
22
  s.email = 'mikebrodrigues@gmail.com'
21
23
 
22
24
  s.test_files = Dir['test/tc*.rb']
23
- s.executables = %w(
25
+ s.executables = %w[
24
26
  onkyo.rb
25
27
  onkyo_server.rb
26
- )
27
-
28
+ ]
28
29
  end
@@ -1,4 +1,6 @@
1
- require_relative '../lib/eiscp/dictionary.rb'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/eiscp/dictionary'
2
4
  require 'minitest/autorun'
3
5
 
4
6
  class TestDictionary < MiniTest::Test
data/test/tc_message.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../lib/eiscp/message'
2
4
  require 'minitest/autorun'
3
5
 
@@ -6,7 +8,10 @@ class TestMessage < MiniTest::Test
6
8
  DISCOVERY_STRING = DISCOVERY_PACKET.to_eiscp
7
9
 
8
10
  def test_create_discovery_iscp_message
9
- assert_equal(EISCP::Message.new(command: 'ECN', value: 'QSTN', terminator: "\r\n", unit_type: 'x', start: '!').to_iscp, '!xECNQSTN')
11
+ assert_equal(
12
+ EISCP::Message.new(command: 'ECN', value: 'QSTN', terminator: "\r\n", unit_type: 'x',
13
+ start: '!').to_iscp, '!xECNQSTN'
14
+ )
10
15
  end
11
16
 
12
17
  def test_create_messages
data/test/tc_parser.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../lib/eiscp/parser'
2
4
  require_relative '../lib/eiscp/message'
3
5
  require 'minitest/autorun'
@@ -27,7 +29,6 @@ class TestParser < MiniTest::Test
27
29
  end
28
30
 
29
31
  def test_return_nil_for_fake_human_readable
30
- assert_equal(EISCP::Parser.parse('fake-command value'), nil)
32
+ assert_nil(EISCP::Parser.parse('fake-command value'))
31
33
  end
32
-
33
34
  end
data/test/tc_receiver.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../lib/eiscp/receiver'
2
4
  require 'minitest/autorun'
3
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onkyo_eiscp_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Rodrigues
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-22 00:00:00.000000000 Z
11
+ date: 2021-03-30 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: "\n Control Onkyo receivers over the network.Use the provided binary
14
14
  or\n require the library for use in your scripts.\n "
@@ -63,13 +63,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
63
  - !ruby/object:Gem::Version
64
64
  version: '0'
65
65
  requirements: []
66
- rubyforge_project:
67
- rubygems_version: 2.4.5
66
+ rubygems_version: 3.2.3
68
67
  signing_key:
69
68
  specification_version: 4
70
69
  summary: Manipulate Onkyo stereos with the eISCP protocol
71
70
  test_files:
72
- - test/tc_parser.rb
73
- - test/tc_receiver.rb
74
71
  - test/tc_dictionary.rb
75
72
  - test/tc_message.rb
73
+ - test/tc_parser.rb
74
+ - test/tc_receiver.rb