onkyo_eiscp_ruby 0.0.2 → 0.0.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: be075bcd7a1830e7614b8384d610ea59d0b7160f
4
+ data.tar.gz: 40cea299c77c937a786f387cfbf4c11e8696632c
5
+ SHA512:
6
+ metadata.gz: 5066d43a66350cb39e84ddbae87038d4ba04057f2dd884eee1852c3dd108ed4a44842878157dfb854e7802d3e771cbe5789a70af936af0e338a36264e92332d6
7
+ data.tar.gz: 6c2c8bb4ba7d3b7e9fee332d448a054cf97480194192252965b363b7efaa8d8b095ae76d2f68ab6f064fbf68ec2bac75c63b2ba53a26e891b055c0751f776cc4
data/README.md CHANGED
@@ -1,41 +1,75 @@
1
1
  onkyo_eiscp_ruby
2
2
  ================
3
+ [![Gem Version](https://badge.fury.io/rb/onkyo_eiscp_ruby.png)](http://badge.fury.io/rb/onkyo_eiscp_ruby)
3
4
 
4
5
  A Ruby implementation of eISCP for controlling Onkyo receivers.
5
6
 
6
7
  **This code is still under heavy development and using it might make you sick.**
7
- * Create ISCP messages and eISCP packets
8
- * Automatically discover receiver's in the broadcast domain
9
- * Send/Recieve eISCP messages
10
- * Open a TCP socket to send commands and receive solicited and non-solicited status updates.
11
- * Mock reciever (currently only responds to discovery)
8
+ Create ISCP messages and eISCP packets
9
+ Automatically discover receiver's in the broadcast domain
10
+ Send/Recieve eISCP messages
11
+ Open a TCP socket to send commands and receive solicited and non-solicited status updates.
12
+ Mock reciever (currently only responds to discovery)
12
13
 
13
14
  **Inspired by https://github.com/miracle2k/onkyo-eiscp
14
15
 
15
- Usage
16
- ________________
16
+ **Protocol information from http://michael.elsdoerfer.name/onkyo/ISCP-V1.21_2011.xls
17
17
 
18
- # require the library
19
18
 
20
- require 'eiscp'
21
19
 
22
-
23
- # Discover local receivers
24
- EISCP.discover
25
-
26
-
27
- # Open a TCP connection to monitor solicited updates
28
- eiscp = EISCP.new('10.0.0.1')
29
- eiscp.connect
20
+ Using the Library
21
+ -----------------
22
+ * require the library
30
23
 
31
- # You can also pass a block and operate on received packet strings:
32
- eiscp.connect do |data|
33
- puts EISCPPacket.parse(data).iscp_message
34
- end
24
+ require 'eiscp'
35
25
 
36
- # Turn on the receiver
37
- iscp_message = ISCPMessage.new("PWR", "01")
38
- eiscp_packet = EISCPPacket.new(iscp_message.message)
39
- eiscp.send(eiscp_packet.to_s)
40
-
26
+ * Discover local receivers
41
27
 
28
+ EISCP::Receiver.discover
29
+
30
+ * Create Receiver object from first discovered
31
+
32
+ Receiver.new
33
+
34
+ * Open a TCP connection to monitor solicited updates
35
+
36
+ receiver = Receiver.new('10.0.0.1')
37
+ receiver.connect
38
+
39
+ * You can also pass a block and operate on received packet strings:
40
+
41
+ receiver.connect do |data|
42
+ puts EISCP::Receiver.parse(data).iscp_message
43
+ end
44
+
45
+ * Turn on the receiver
46
+
47
+ message = EISCP::Message.parse("PWR", "01")
48
+ message.send(message.to_eiscp)
49
+
50
+ * New 'parse' method makes creating EISCP objects more flexible.
51
+ This parses messages from command line or raw eiscp data from the socket
52
+
53
+ iscp_message = EISCP::Message.parse "PWR01"
54
+ iscp_message = EISCP::Message.parse "PWR 01"
55
+ iscp_message = EISCP::Message.parse "!1PWR01"
56
+ iscp_message = EISCP::Message.parse "!1PWR 01"
57
+
58
+ * Parsing raw socket data
59
+
60
+ iscp_message_from_raw_eiscp = EISCP::Message.parse iscp_message.to_eiscp
61
+
62
+ Using the Binaries
63
+ ------------------
64
+
65
+ * Discover local receivers
66
+
67
+ $ onkyo.rb -d
68
+
69
+ * Connect to the first discovered receiver to see status updates
70
+
71
+ $ onkyo.rb -c
72
+
73
+ * Start the mock server (only responds to 'ECNQSTN')
74
+
75
+ $ onkyo-server.rb
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
data/bin/onkyo-server.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'eiscp/eiscp_server'
3
+ require 'eiscp/mock_receiver'
4
4
 
5
5
  puts "Starting server on 60128..."
6
6
 
7
- EISCPServer.new
7
+ EISCP::MockReceiver.new
data/bin/onkyo.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require 'eiscp'
4
4
  require 'optparse'
5
5
  require 'ostruct'
6
-
7
6
  class Options
8
7
  DEFAULT_OPTIONS = { verbose: true, all: false }
9
8
  USAGE = ' Usage: onkyo_rb [options]'
@@ -48,8 +47,8 @@ class Options
48
47
  end
49
48
 
50
49
  if @options.discover
51
- EISCP.discover.each do |receiver|
52
- puts EISCPPacket.parse(receiver[0]).to_s
50
+ EISCP::Receiver.discover.each do |receiver|
51
+ puts EISCP::Message.parse(receiver[0]).to_iscp
53
52
  end
54
53
  exit 0
55
54
  end
@@ -60,8 +59,10 @@ class Options
60
59
  end
61
60
 
62
61
  if @options.connect
63
- eiscp = EISCP.new(EISCP.discover[0][1])
64
- eiscp.connect
62
+ eiscp = EISCP::Receiver.new(EISCP::Receiver.discover[0][1])
63
+ eiscp.connect do |data|
64
+ puts msg = EISCP::Receiver.parse(data).to_iscp
65
+ end
65
66
  end
66
67
 
67
68
  if ARGV == []
@@ -76,8 +77,9 @@ end
76
77
  @options = Options.parse(ARGV)
77
78
 
78
79
 
79
- eiscp = EISCP.new(EISCP.discover[0][1])
80
- eiscp.send_recv(EISCPPacket.new(ISCPMessage.new(ARGV[0], ARGV[1]).message).to_s)
80
+ receiver = EISCP::Receiver.new(EISCP::Receiver.discover[0][1])
81
+ message = (EISCP::Message.parse(ARGV.join(" ")).to_eiscp)
82
+ puts receiver.send_recv message
81
83
 
82
84
 
83
85
 
data/lib/eiscp/command.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require 'yaml'
2
- require 'eiscp/eiscp'
2
+ require 'eiscp/receiver'
3
3
  require 'ostruct'
4
4
 
5
5
  module Command
@@ -11,19 +11,12 @@ module Command
11
11
  @@zones = @@yaml_object.map{|k, v| k}
12
12
  @@zones.each {|zone| class_variable_set("@@#{zone}", nil) }
13
13
  @@main = @@yaml_object['main']
14
-
15
-
16
- @@zones.each do |zone|
17
- Command.class_variable_set("@@#{zone}", "[]")
18
- end
19
-
20
-
21
14
 
22
15
  def self.command_to_name(command)
23
16
  return @@main[command]['name']
24
17
  end
25
18
 
26
- def self.name_to_command(name)
19
+ def self.command_name_to_command(name)
27
20
  @@main.each_pair do |command, attrs|
28
21
  if attrs['name'] == name
29
22
  return command
@@ -44,10 +37,10 @@ module Command
44
37
  end
45
38
 
46
39
 
47
- def self.description_from_name(name)
40
+ def self.description_from_command_name(name)
48
41
  @@main.each_pair do |command, attrs|
49
42
  if attrs['name'] == name
50
- return command['description']
43
+ return @@main[command]['description']
51
44
  end
52
45
  end
53
46
  end
@@ -73,8 +66,34 @@ module Command
73
66
  end
74
67
  end
75
68
 
76
- def self.list_compatible_commands
69
+ def self.list_compatible_commands(modelstring)
70
+ sets = []
71
+ @@modelsets.each_pair do |set, array|
72
+ if array.include? modelstring
73
+ sets << set
74
+ end
75
+ end
76
+ return sets
77
+ end
77
78
 
79
+ def self.parse(string)
80
+ array = string.split(" ")
81
+ zone = 'main'
82
+ command_name = ''
83
+ parameter_name = ''
84
+ if array.count == 3
85
+ zone = array.shift
86
+ command_name = array.shift
87
+ parameter_name = array.shift
88
+ elsif array.count == 2
89
+ command_name = array.shift
90
+ parameter_name = array.shift
91
+ end
92
+ command = Command.command_name_to_command(command_name)
93
+ parameter = Command.command_value_name_to_value(command, parameter_name)
94
+ return EISCP::Message.new(command, parameter)
78
95
  end
96
+
79
97
  end
80
98
 
99
+
@@ -0,0 +1,99 @@
1
+ module EISCP
2
+ class Message
3
+
4
+ # EISCP header
5
+ attr_accessor :header
6
+ MAGIC = "ISCP"
7
+ HEADER_SIZE = 16
8
+ ISCP_VERSION = "\x01"
9
+ RESERVED = "\x00\x00\x00"
10
+
11
+
12
+ # ISCP attrs
13
+ attr_accessor :start
14
+ attr_accessor :unit_type
15
+ attr_accessor :command
16
+ attr_accessor :parameter
17
+ attr_reader :iscp_message
18
+
19
+
20
+ # REGEX
21
+ REGEX = /(?<start>!)?(?<unit_type>(\d|x))?(?<command>[A-Z]{3})\s?(?<parameter>.*)(?<end>\x1A)?/
22
+
23
+ def initialize(command, parameter, unit_type = "1", start = "!")
24
+ if unit_type == nil
25
+ @unit_type = "1"
26
+ else
27
+ @unit_type = unit_type
28
+ end
29
+ if start == nil
30
+ @start = "!"
31
+ else
32
+ @start = start
33
+ end
34
+ @command = command
35
+ @parameter = parameter
36
+ @iscp_message = [ @start, @unit_type, @command, @parameter ].inject(:+)
37
+ @header = { :magic => MAGIC,
38
+ :header_size => HEADER_SIZE,
39
+ :data_size => @iscp_message.length,
40
+ :version => ISCP_VERSION,
41
+ :reserved => RESERVED
42
+ }
43
+ end
44
+
45
+
46
+ # Check if two messages send the same command
47
+ def ==(message_object)
48
+ self.iscp_message == message_object.iscp_message ? true : false
49
+ end
50
+ # Identifies message format, calls appropriate parse function
51
+ # returns Message object.
52
+
53
+ def self.parse(string)
54
+ case string
55
+ when /^ISCP/
56
+ parse_eiscp_string(string)
57
+ when REGEX
58
+ parse_iscp_message(string)
59
+ else
60
+ puts "Not a valid ISCP or EISCP message."
61
+ end
62
+ end
63
+
64
+
65
+
66
+ # ISCP Message string parser
67
+
68
+ def self.parse_iscp_message(msg_string)
69
+ match = msg_string.match(REGEX)
70
+ Message.new(match[:command], match[:parameter], match[:unit_type], match[:start])
71
+ end
72
+
73
+ #parse eiscp_message string
74
+ def self.parse_eiscp_string(eiscp_message_string)
75
+ array = eiscp_message_string.unpack("A4NNAa3A*")
76
+ iscp_message = Message.parse_iscp_message(array[5])
77
+ packet = Message.new(iscp_message.command, iscp_message.parameter, iscp_message.unit_type, iscp_message.start)
78
+ packet.header = {
79
+ :magic => array[0],
80
+ :header_size => array[1],
81
+ :data_size => array[2],
82
+ :version => array[3],
83
+ :reserved => array[4]
84
+ }
85
+ return packet
86
+ end
87
+
88
+ # Return ISCP Message string
89
+ def to_iscp
90
+ return "#{@start + @unit_type + @command + @parameter}"
91
+ end
92
+
93
+ # Return EISCP Message string
94
+ def to_eiscp
95
+ return [ @header[:magic], @header[:header_size], @header[:data_size], @header[:version], @header[:reserved], @iscp_message.to_s ].pack("A4NNAa3A*")
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,22 @@
1
+ require 'socket'
2
+ require 'eiscp/receiver'
3
+ require 'eiscp/message'
4
+
5
+ # Mock server that only responds to ECNQSTN.
6
+
7
+ module EISCP
8
+ class MockReceiver
9
+
10
+ ONKYO_DISCOVERY_RESPONSE = Message.new("ECN", "TX-NR609/60128/DX/001122334455")
11
+
12
+ # Create/start the server object.
13
+
14
+ def initialize
15
+ Socket.udp_server_loop("255.255.255.255", EISCP::ONKYO_PORT) do |msg, msg_src|
16
+ msg_src.reply ONKYO_DISCOVERY_RESPONSE.to_eiscp
17
+ puts msg
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,180 @@
1
+ require 'socket'
2
+ require 'eiscp/message'
3
+ require 'resolv'
4
+
5
+ module EISCP
6
+ class Receiver
7
+
8
+ attr_accessor :host
9
+ attr_accessor :model
10
+ attr_accessor :port
11
+ attr_accessor :area
12
+ attr_accessor :mac_address
13
+
14
+ ONKYO_MAGIC = Message.new("ECN", "QSTN", "x").to_eiscp
15
+ ONKYO_PORT = 60128
16
+
17
+ # Create a new EISCP object to communicate with a receiver.
18
+ # If no host is given, use auto discovery and create a
19
+ # receiver object using the first host to respond.
20
+
21
+ def initialize(host = nil, port = ONKYO_PORT)
22
+ if host == nil
23
+ if first_rec = self.class.discover[0]
24
+ host = first_rec[1]
25
+ set_info first_rec[0]
26
+ else
27
+ raise Exception
28
+ end
29
+ end
30
+ @host = Resolv.getaddress host
31
+ @port = port
32
+ unless @model
33
+ set_info get_ecn
34
+ end
35
+ end
36
+
37
+ def set_info(ecn_string)
38
+ array = self.class.parse_ecn(ecn_string)
39
+ @model = array.shift
40
+ @port = array.shift.to_i
41
+ @area = array.shift
42
+ @mac_address = array.shift.split("\x19")[0]
43
+ return self
44
+ end
45
+
46
+ def get_ecn
47
+ self.class.discover.each do |entry|
48
+ if @host == entry[1]
49
+ return entry[0]
50
+ end
51
+ end
52
+ end
53
+
54
+ # Gets the ECNQSTN response of self using @host
55
+ # then parses it with parse_ecn, returning an array
56
+ # with receiver info
57
+
58
+ def get_ecn_array
59
+ self.class.discover.each do |entry|
60
+ if @host == entry[1]
61
+ array = self.class.parse_ecn(entry[0])
62
+ end
63
+ return array
64
+ end
65
+ end
66
+
67
+ # Returns array containing @model, @port, @area, and @mac_address
68
+ # from ECNQSTN response
69
+
70
+ def self.parse_ecn(ecn_string)
71
+ message = EISCP::Message.parse(ecn_string)
72
+ message.parameter.split("/")
73
+ end
74
+
75
+ # Internal method for receiving data with a timeout
76
+
77
+ def self.recv(sock, timeout = 0.5)
78
+ data = []
79
+ while true
80
+ ready = IO.select([sock], nil, nil, timeout)
81
+ if ready != nil
82
+ then readable = ready[0]
83
+ else
84
+ return data
85
+ end
86
+
87
+
88
+ readable.each do |socket|
89
+ begin
90
+ if socket == sock
91
+ data << sock.recv_nonblock(1024).chomp
92
+ end
93
+ rescue IO::WaitReadable
94
+ retry
95
+ end
96
+ end
97
+
98
+ end
99
+ end
100
+
101
+ # Returns an array of arrays consisting of a discovery response packet string
102
+ # and the source ip address of the reciever.
103
+
104
+ def self.discover
105
+ sock = UDPSocket.new
106
+ sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
107
+ sock.send(ONKYO_MAGIC, 0, '<broadcast>', ONKYO_PORT)
108
+ data = []
109
+ while true
110
+ ready = IO.select([sock], nil, nil, 0.5)
111
+ if ready != nil
112
+ then readable = ready[0]
113
+ else
114
+ return data
115
+ end
116
+
117
+
118
+ readable.each do |socket|
119
+ begin
120
+ if socket == sock
121
+ msg, addr = sock.recvfrom_nonblock(1024)
122
+ data << [msg, addr[2]]
123
+ end
124
+ rescue IO::WaitReadable
125
+ retry
126
+ end
127
+ end
128
+
129
+ end
130
+ end
131
+
132
+ # Sends a packet string on the network
133
+
134
+ def send(eiscp_packet)
135
+ sock = TCPSocket.new @host, @port
136
+ sock.puts eiscp_packet
137
+ sock.close
138
+ end
139
+
140
+ # Send a packet string and return recieved data string.
141
+
142
+ def send_recv(eiscp_packet)
143
+ sock = TCPSocket.new @host, @port
144
+ sock.puts eiscp_packet
145
+ return Receiver.recv(sock, 0.5)
146
+ end
147
+
148
+ # Open a TCP connection to the host and print all received messages until
149
+ # killed.
150
+
151
+ def connect(&block)
152
+ sock = TCPSocket.new @host, @port
153
+ while true
154
+ ready = IO.select([sock], nil, nil, nil)
155
+ if ready != nil
156
+ then readable = ready[0]
157
+ else
158
+ return
159
+ end
160
+
161
+ readable.each do |socket|
162
+ begin
163
+ if socket == sock
164
+ data = sock.recv_nonblock(1024).chomp
165
+ if block_given?
166
+ yield data
167
+ else
168
+ puts data
169
+ end
170
+ end
171
+ rescue IO::WaitReadable
172
+ retry
173
+ end
174
+ end
175
+
176
+ end
177
+ end
178
+
179
+ end
180
+ end
data/lib/eiscp.rb CHANGED
@@ -1,10 +1,9 @@
1
- # Create and send EISCP messages to control Onkyo receivers.
1
+ # Library for controlling Onkyo receivers over TCP/IP.
2
2
 
3
- class EISCP
4
- VERSION = '0.0.2'
3
+ module EISCP
4
+ VERSION = '0.0.3'
5
5
  end
6
6
 
7
- require 'eiscp/eiscp'
8
- require 'eiscp/eiscp_packet'
9
- require 'eiscp/iscp_message'
7
+ require 'eiscp/receiver'
8
+ require 'eiscp/message'
10
9
  require 'eiscp/command'
@@ -11,7 +11,8 @@ Gem::Specification.new do |s|
11
11
  s.homepage = "https://github.com/mikerodrigues/onkyo_eiscp_ruby"
12
12
 
13
13
  s.description = %q(
14
- Use the provided binary script or require the library for use in your scripts.
14
+ Control Onkyo receivers over the network.Use the provided binary script or
15
+ require the library for use in your scripts.
15
16
  )
16
17
 
17
18
  s.author = "Michael Rodrigues"
data/test/tc_command.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require_relative "../lib/eiscp/command.rb"
2
+ require_relative "../lib/eiscp/message.rb"
2
3
  require "test/unit"
3
4
 
4
5
  class TestCommand < Test::Unit::TestCase
@@ -7,8 +8,8 @@ class TestCommand < Test::Unit::TestCase
7
8
  assert_equal(Command.command_to_name("PWR"), "system-power")
8
9
  end
9
10
 
10
- def test_name_to_command
11
- assert_equal(Command.name_to_command("system-power"), "PWR")
11
+ def test_command_name_to_command
12
+ assert_equal(Command.command_name_to_command("system-power"), "PWR")
12
13
  end
13
14
 
14
15
  def test_command_value_to_value_name
@@ -19,12 +20,24 @@ class TestCommand < Test::Unit::TestCase
19
20
  assert_equal(Command.command_value_name_to_value("PWR", "on"), "01")
20
21
  end
21
22
 
22
- def test_description_from_name
23
- assert_equal(Command.description_from_name("system-power"), "System Power Command")
23
+ def test_description_from_command_name
24
+ assert_equal(Command.description_from_command_name("system-power"), "System Power Command")
24
25
  end
25
26
 
26
27
  def test_description_from_command
27
28
  assert_equal(Command.description_from_command("PWR"), "System Power Command")
28
29
  end
29
30
 
31
+ def test_parse_system_power
32
+ assert_equal(Command.parse('system-power on'), EISCP::Message.parse('PWR01'))
33
+ end
34
+
35
+ def test_parse_zone2_system_power
36
+ assert_equal(Command.parse('zone2 power on'), EISCP::Message.parse('ZPW01'))
37
+ end
38
+
39
+ def test_parse_volume_as_integer
40
+ assert_equal(Command.parse('main-volume 25'), EISCP::Message.parse('MVL19'))
41
+ end
42
+
30
43
  end
@@ -0,0 +1,27 @@
1
+ require_relative "../lib/eiscp/message"
2
+ require "test/unit"
3
+
4
+ class TestMessage < Test::Unit::TestCase
5
+
6
+
7
+ DISCOVERY_PACKET = EISCP::Message.new('ECN', 'QSTN', 'x', '!')
8
+ DISCOVERY_STRING = DISCOVERY_PACKET.to_eiscp
9
+
10
+
11
+ def test_create_discovery_iscp_message
12
+ assert_equal(EISCP::Message.new("ECN", "QSTN", "x", "!").to_iscp, "!xECNQSTN")
13
+ end
14
+
15
+ def test_parse_discovery_iscp_message
16
+ assert_equal(EISCP::Message.parse("!xECNQSTN").to_iscp, "!xECNQSTN")
17
+ end
18
+
19
+ def test_create_discovery_packet_string
20
+ assert_equal(DISCOVERY_PACKET.to_eiscp, DISCOVERY_STRING)
21
+ end
22
+
23
+ def test_parse_discovery_packet_string
24
+ assert_equal(EISCP::Message.parse(DISCOVERY_STRING).to_eiscp, DISCOVERY_PACKET.to_eiscp)
25
+ end
26
+
27
+ end
@@ -1,4 +1,4 @@
1
- require_relative "../lib/eiscp/eiscp.rb"
1
+ require_relative "../lib/eiscp/receiver"
2
2
  require "test/unit"
3
3
 
4
4
  class TestEISCP
metadata CHANGED
@@ -1,18 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: onkyo_eiscp_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
- prerelease:
4
+ version: 0.0.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Michael Rodrigues
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-01-02 00:00:00.000000000 Z
11
+ date: 2014-01-20 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: ! "\n Use the provided binary script or require the library for use
15
- in your scripts.\n "
13
+ description: "\n Control Onkyo receivers over the network.Use the provided binary
14
+ script or\n require the library for use in your scripts.\n "
16
15
  email: mikebrodrigues@gmail.com
17
16
  executables:
18
17
  - onkyo.rb
@@ -21,49 +20,44 @@ extensions: []
21
20
  extra_rdoc_files:
22
21
  - README.md
23
22
  files:
23
+ - README.md
24
+ - VERSION
24
25
  - bin/onkyo-server.rb
25
26
  - bin/onkyo.rb
26
- - lib/eiscp/eiscp_server.rb
27
- - lib/eiscp/eiscp.rb
28
- - lib/eiscp/eiscp_packet.rb
29
- - lib/eiscp/command.rb
30
- - lib/eiscp/iscp_message.rb
27
+ - eiscp-commands.yaml
31
28
  - lib/eiscp.rb
32
- - test/tc_command.rb
33
- - test/tc_iscp_message.rb
34
- - test/tc_eiscp.rb
35
- - test/tc_eiscp_packet.rb
36
- - VERSION
29
+ - lib/eiscp/command.rb
30
+ - lib/eiscp/message.rb
31
+ - lib/eiscp/mock_receiver.rb
32
+ - lib/eiscp/receiver.rb
37
33
  - onkyo_eiscp_ruby.gemspec
38
- - eiscp-commands.yaml
39
- - README.md
34
+ - test/tc_command.rb
35
+ - test/tc_message.rb
36
+ - test/tc_receiver.rb
40
37
  homepage: https://github.com/mikerodrigues/onkyo_eiscp_ruby
41
38
  licenses: []
39
+ metadata: {}
42
40
  post_install_message:
43
41
  rdoc_options: []
44
42
  require_paths:
45
43
  - lib
46
44
  required_ruby_version: !ruby/object:Gem::Requirement
47
- none: false
48
45
  requirements:
49
- - - ! '>='
46
+ - - ">="
50
47
  - !ruby/object:Gem::Version
51
48
  version: '0'
52
49
  required_rubygems_version: !ruby/object:Gem::Requirement
53
- none: false
54
50
  requirements:
55
- - - ! '>='
51
+ - - ">="
56
52
  - !ruby/object:Gem::Version
57
53
  version: '0'
58
54
  requirements: []
59
55
  rubyforge_project:
60
- rubygems_version: 1.8.25
56
+ rubygems_version: 2.2.0
61
57
  signing_key:
62
- specification_version: 3
58
+ specification_version: 4
63
59
  summary: Manipulate Onkyo stereos with the eISCP protocol
64
60
  test_files:
61
+ - test/tc_message.rb
62
+ - test/tc_receiver.rb
65
63
  - test/tc_command.rb
66
- - test/tc_iscp_message.rb
67
- - test/tc_eiscp.rb
68
- - test/tc_eiscp_packet.rb
69
- has_rdoc:
data/lib/eiscp/eiscp.rb DELETED
@@ -1,125 +0,0 @@
1
- require 'socket'
2
- require 'eiscp/eiscp_packet.rb'
3
- require 'eiscp/iscp_message.rb'
4
-
5
- class EISCP
6
- ONKYO_PORT = 60128
7
- ONKYO_MAGIC = EISCPPacket.new("ECN", "QSTN", "x").to_s
8
-
9
- # Create a new EISCP object to communicate with a receiver.
10
-
11
- def initialize(host)
12
- @host = host
13
- end
14
-
15
- # Internal method for receiving data with a timeout
16
-
17
- def self.recv(sock, timeout = 0.5)
18
- data = []
19
- while true
20
- ready = IO.select([sock], nil, nil, timeout)
21
- if ready != nil
22
- then readable = ready[0]
23
- else
24
- return data
25
- end
26
-
27
-
28
- readable.each do |socket|
29
- begin
30
- if socket == sock
31
- data << sock.recv_nonblock(1024).chomp
32
- end
33
- rescue IO::WaitReadable
34
- retry
35
- end
36
- end
37
-
38
- end
39
- end
40
-
41
- # Returns an array of arrays consisting of a discovery response packet string
42
- # and the source ip address of the reciever.
43
-
44
- def self.discover
45
- sock = UDPSocket.new
46
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
47
- sock.send(ONKYO_MAGIC, 0, '<broadcast>', ONKYO_PORT)
48
- data = []
49
- while true
50
- ready = IO.select([sock], nil, nil, 0.5)
51
- if ready != nil
52
- then readable = ready[0]
53
- else
54
- return data
55
- end
56
-
57
-
58
- readable.each do |socket|
59
- begin
60
- if socket == sock
61
- msg, addr = sock.recvfrom_nonblock(1024)
62
- data << [msg, addr[2]]
63
- end
64
- rescue IO::WaitReadable
65
- retry
66
- end
67
- end
68
-
69
- end
70
- end
71
-
72
- # Sends a packet string on the network
73
-
74
- def send(eiscp_packet)
75
- sock = TCPSocket.new @host, ONKYO_PORT
76
- sock.puts eiscp_packet
77
- sock.close
78
- end
79
-
80
- # Send a packet string and return recieved data string.
81
-
82
- def send_recv(eiscp_packet)
83
- sock = TCPSocket.new @host, ONKYO_PORT
84
- sock.puts eiscp_packet
85
- puts EISCP.recv(sock, 0.5)
86
- end
87
-
88
- # Open a TCP connection to the host and print all received messages until
89
- # killed.
90
-
91
- def connect(&block)
92
- sock = TCPSocket.new @host, ONKYO_PORT
93
- while true
94
- ready = IO.select([sock], nil, nil, nil)
95
- if ready != nil
96
- then readable = ready[0]
97
- else
98
- return
99
- end
100
-
101
- readable.each do |socket|
102
- begin
103
- if socket == sock
104
- data = sock.recv_nonblock(1024).chomp
105
- if block_given?
106
- yield data
107
- else
108
- puts data
109
- end
110
- end
111
- rescue IO::WaitReadable
112
- retry
113
- end
114
- end
115
-
116
- end
117
- end
118
-
119
- end
120
-
121
-
122
-
123
-
124
-
125
-
@@ -1,51 +0,0 @@
1
- require 'eiscp/iscp_message'
2
-
3
- # Public: Encapsulates ISCP Messages in eISCP packets to send on the network.
4
- # You can alsoe use the class method 'parse' to create objects from strings
5
- # captured from the network using the EISCP class.
6
-
7
- class EISCPPacket
8
-
9
- MAGIC = "ISCP"
10
- HEADER_SIZE = 16
11
- VERSION = "\x01"
12
- RESERVED = "\x00\x00\x00"
13
-
14
- attr_accessor :header
15
- attr_reader :iscp_message
16
-
17
- # Create a new EISCPPacket object with a given command and parameter
18
- # -+command+ - an ISCP command like "PWR" for "system-power"
19
- # -+parameter+ - an ISCP command parameter like "01" for "on" for
20
- # "system-power"
21
-
22
- def initialize(command, parameter, unit_type = "1", start = "!")
23
- @iscp_message = ISCPMessage.new(command, parameter, unit_type, start)
24
- @header = { :magic => MAGIC, :header_size => HEADER_SIZE, :data_size => @iscp_message.to_s.length, :version => VERSION, :reserved => RESERVED }
25
- end
26
-
27
- # Returns a raw packet string for sending on the network using EISCP object.
28
-
29
- def to_s
30
- return [ @header[:magic], @header[:header_size], @header[:data_size], @header[:version], @header[:reserved], @iscp_message.to_s ].pack("A4NNAa3A*")
31
- end
32
-
33
- # Returns an EISCPPacket from a raw packet string
34
-
35
- def self.parse(eiscp_message_string)
36
- array = eiscp_message_string.unpack("A4NNAa3A*")
37
- iscp_message = ISCPMessage.parse(array[5])
38
- packet = EISCPPacket.new(iscp_message.command, iscp_message.parameter, iscp_message.unit_type, iscp_message.start)
39
- packet.header = {
40
- :magic => array[0],
41
- :header_size => array[1],
42
- :data_size => array[2],
43
- :version => array[3],
44
- :reserved => array[4]
45
- }
46
- return packet
47
- end
48
-
49
- end
50
-
51
-
@@ -1,21 +0,0 @@
1
- require 'socket'
2
- require 'eiscp/eiscp_packet'
3
- require 'eiscp/eiscp'
4
-
5
- # Mock server that only responds to ECNQSTN.
6
-
7
- class EISCPServer
8
-
9
- ONKYO_DISCOVERY_RESPONSE = EISCPPacket.new("ECN", "TX-NR609/60128/DX/001122334455")
10
-
11
- # Create/start the server object.
12
-
13
- def initialize
14
- Socket.udp_server_loop("255.255.255.255", EISCP::ONKYO_PORT) do |msg, msg_src|
15
- msg_src.reply ONKYO_DISCOVERY_RESPONSE.to_s
16
- puts msg
17
- end
18
- end
19
-
20
- end
21
-
@@ -1,24 +0,0 @@
1
- class ISCPMessage
2
-
3
- attr_accessor :start
4
- attr_accessor :unit_type
5
- attr_accessor :command
6
- attr_accessor :parameter
7
-
8
- def initialize(command, parameter, unit_type = "1", start = "!")
9
- @unit_type = unit_type
10
- @start = start
11
- @command = command
12
- @parameter = parameter
13
- end
14
-
15
- def self.parse(msg_string)
16
- match = msg_string.match(/(?<start>!)(?<unit_type>\w)(?<command>[A-Z]{3})(?<parameter>\S+)/)
17
- ISCPMessage.new(match[:command], match[:parameter], match[:unit_type], match[:start])
18
- end
19
-
20
- def to_s
21
- return "#{@start + @unit_type + @command + @parameter}\r"
22
- end
23
-
24
- end
@@ -1,17 +0,0 @@
1
- require_relative "../lib/eiscp/eiscp_packet.rb"
2
- require "test/unit"
3
-
4
- class TestEISCPPacket < Test::Unit::TestCase
5
-
6
- DISCOVERY_STRING = "ISCP\x00\x00\x00\x10\x00\x00\x00\n\x01\x00\x00\x00!xECNQSTN\r"
7
- DISCOVERY_PACKET = EISCPPacket.new('ECN', 'QSTN', 'x', '!')
8
-
9
- def test_create_discovery_packet_string
10
- assert_equal(DISCOVERY_PACKET.to_s, DISCOVERY_STRING)
11
- end
12
-
13
- def test_parse_discovery_packet_string
14
- assert_equal(EISCPPacket.parse(DISCOVERY_STRING).to_s, DISCOVERY_PACKET.to_s)
15
- end
16
-
17
- end
@@ -1,14 +0,0 @@
1
- require_relative "../lib/eiscp/iscp_message.rb"
2
- require "test/unit"
3
-
4
- class TestISCPMessage < Test::Unit::TestCase
5
-
6
- def test_create_discovery_iscp_message
7
- assert_equal(ISCPMessage.new("ECN", "QSTN", "x", "!").to_s, "!xECNQSTN\r")
8
- end
9
-
10
- def test_parse_discovery_iscp_message
11
- assert_equal(ISCPMessage.parse("!xECNQSTN\r").to_s, "!xECNQSTN\r")
12
- end
13
-
14
- end