onkyo_eiscp_ruby 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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