onkyo_eiscp_ruby 0.0.1
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.
- data/README.md +41 -0
- data/VERSION +1 -0
- data/bin/onkyo.rb +84 -0
- data/eiscp-commands.yaml +11740 -0
- data/lib/eiscp/command.rb +80 -0
- data/lib/eiscp/eiscp.rb +113 -0
- data/lib/eiscp/eiscp_packet.rb +38 -0
- data/lib/eiscp/iscp_message.rb +24 -0
- data/lib/eiscp.rb +9 -0
- data/lib/server.rb +15 -0
- data/onkyo_eiscp_ruby.gemspec +25 -0
- data/test/tc_command.rb +30 -0
- data/test/tc_eiscp.rb +6 -0
- data/test/tc_eiscp_packet.rb +17 -0
- data/test/tc_iscp_message.rb +14 -0
- metadata +67 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'eiscp/eiscp'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Command
|
6
|
+
|
7
|
+
@@yaml_file_path = File.join(File.expand_path(File.dirname(__FILE__)), '../../eiscp-commands.yaml')
|
8
|
+
@@yaml_object = YAML.load(File.read(@@yaml_file_path))
|
9
|
+
@@modelsets = @@yaml_object["modelsets"]
|
10
|
+
@@yaml_object.delete("modelsets")
|
11
|
+
@@zones = @@yaml_object.map{|k, v| k}
|
12
|
+
@@zones.each {|zone| class_variable_set("@@#{zone}", nil) }
|
13
|
+
@@main = @@yaml_object['main']
|
14
|
+
|
15
|
+
|
16
|
+
@@zones.each do |zone|
|
17
|
+
Command.class_variable_set("@@#{zone}", "[]")
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
def self.command_to_name(command)
|
23
|
+
return @@main[command]['name']
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.name_to_command(name)
|
27
|
+
@@main.each_pair do |command, attrs|
|
28
|
+
if attrs['name'] == name
|
29
|
+
return command
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.command_value_to_value_name(command, value)
|
35
|
+
return @@main[command]['values'][value]['name']
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.command_value_name_to_value(command, name)
|
39
|
+
@@main[command]['values'].each do |k, v|
|
40
|
+
if v['name'] == name.to_s
|
41
|
+
return k
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def self.description_from_name(name)
|
48
|
+
@@main.each_pair do |command, attrs|
|
49
|
+
if attrs['name'] == name
|
50
|
+
return command['description']
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.description_from_command(command)
|
56
|
+
return @@main[command]['description']
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.description_from_command_value(command, value)
|
60
|
+
return @@main[command]['values'].select do |k, v|
|
61
|
+
if k == value
|
62
|
+
return v['description']
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.list_all_commands
|
68
|
+
@@main.each_pair do |command, attrs|
|
69
|
+
puts "#{command} - #{attrs['name']}: #{attrs['description']}"
|
70
|
+
attrs['values'].each_pair do |k, v|
|
71
|
+
puts "--#{k}:#{v}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.list_compatible_commands
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
data/lib/eiscp/eiscp.rb
ADDED
@@ -0,0 +1,113 @@
|
|
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
|
+
def initialize(host)
|
10
|
+
@host = host
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def self.recv(sock, timeout = 0.5)
|
15
|
+
data = []
|
16
|
+
while true
|
17
|
+
ready = IO.select([sock], nil, nil, timeout)
|
18
|
+
if ready != nil
|
19
|
+
then readable = ready[0]
|
20
|
+
else
|
21
|
+
return data
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
readable.each do |socket|
|
26
|
+
begin
|
27
|
+
if socket == sock
|
28
|
+
data << sock.recv_nonblock(1024).chomp
|
29
|
+
end
|
30
|
+
rescue IO::WaitReadable
|
31
|
+
retry
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.discover
|
39
|
+
sock = UDPSocket.new
|
40
|
+
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
|
41
|
+
sock.send(ONKYO_MAGIC, 0, '<broadcast>', ONKYO_PORT)
|
42
|
+
data = []
|
43
|
+
while true
|
44
|
+
ready = IO.select([sock], nil, nil, 0.5)
|
45
|
+
if ready != nil
|
46
|
+
then readable = ready[0]
|
47
|
+
else
|
48
|
+
return data
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
readable.each do |socket|
|
53
|
+
begin
|
54
|
+
if socket == sock
|
55
|
+
msg, addr = sock.recvfrom_nonblock(1024)
|
56
|
+
data << [msg, addr[2]]
|
57
|
+
end
|
58
|
+
rescue IO::WaitReadable
|
59
|
+
retry
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def send(eiscp_packet)
|
67
|
+
sock = TCPSocket.new @host, ONKYO_PORT
|
68
|
+
sock.puts eiscp_packet
|
69
|
+
sock.close
|
70
|
+
end
|
71
|
+
|
72
|
+
def send_recv(eiscp_packet)
|
73
|
+
sock = TCPSocket.new @host, ONKYO_PORT
|
74
|
+
sock.puts eiscp_packet
|
75
|
+
puts EISCP.recv(sock, 0.5)
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def connect(&block)
|
80
|
+
sock = TCPSocket.new @host, ONKYO_PORT
|
81
|
+
while true
|
82
|
+
ready = IO.select([sock], nil, nil, nil)
|
83
|
+
if ready != nil
|
84
|
+
then readable = ready[0]
|
85
|
+
else
|
86
|
+
return
|
87
|
+
end
|
88
|
+
|
89
|
+
readable.each do |socket|
|
90
|
+
begin
|
91
|
+
if socket == sock
|
92
|
+
data = sock.recv_nonblock(1024).chomp
|
93
|
+
if block_given?
|
94
|
+
yield data
|
95
|
+
else
|
96
|
+
puts data
|
97
|
+
end
|
98
|
+
end
|
99
|
+
rescue IO::WaitReadable
|
100
|
+
retry
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'eiscp/iscp_message'
|
2
|
+
|
3
|
+
class EISCPPacket
|
4
|
+
|
5
|
+
MAGIC = "ISCP"
|
6
|
+
HEADER_SIZE = 16
|
7
|
+
VERSION = "\x01"
|
8
|
+
RESERVED = "\x00\x00\x00"
|
9
|
+
|
10
|
+
attr_accessor :header
|
11
|
+
attr_reader :iscp_message
|
12
|
+
|
13
|
+
def initialize(command, parameter, unit_type = "1", start = "!")
|
14
|
+
@iscp_message = ISCPMessage.new(command, parameter, unit_type, start)
|
15
|
+
@header = { :magic => MAGIC, :header_size => HEADER_SIZE, :data_size => @iscp_message.to_s.length, :version => VERSION, :reserved => RESERVED }
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
return [ @header[:magic], @header[:header_size], @header[:data_size], @header[:version], @header[:reserved], @iscp_message.to_s ].pack("A4NNAa3A*")
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse(eiscp_message_string)
|
23
|
+
array = eiscp_message_string.unpack("A4NNAa3A*")
|
24
|
+
iscp_message = ISCPMessage.parse(array[5])
|
25
|
+
packet = EISCPPacket.new(iscp_message.command, iscp_message.parameter, iscp_message.unit_type, iscp_message.start)
|
26
|
+
packet.header = {
|
27
|
+
:magic => array[0],
|
28
|
+
:header_size => array[1],
|
29
|
+
:data_size => array[2],
|
30
|
+
:version => array[3],
|
31
|
+
:reserved => array[4]
|
32
|
+
}
|
33
|
+
return packet
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
|
@@ -0,0 +1,24 @@
|
|
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
|
data/lib/eiscp.rb
ADDED
data/lib/server.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
class EISCPServer
|
4
|
+
|
5
|
+
ONKYO_DISCOVERY_RESPONSE = "ISCP\u0000\u0000\u0000\u0010\u0000\u0000\u0000&\u0001\u0000\u0000\u0000!1ECNTX-MR909/60128/DX/0009B0C727FE\u0019\r\n\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
|
6
|
+
def initialize
|
7
|
+
Socket.udp_server_loop("255.255.255.255", 60128) do |msg, msg_src|
|
8
|
+
msg_src.reply ONKYO_DISCOVERY_RESPONSE
|
9
|
+
puts msg
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
EISCPServer.new
|
15
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'onkyo_eiscp_ruby'
|
3
|
+
s.version = File.read(File.expand_path('VERSION', File.dirname(__FILE__))).strip
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.summary = 'Manipulate Onkyo stereos with the eISCP protocol'
|
6
|
+
s.files = Dir.glob('{bin,config,lib,test,doc}/**/*') +
|
7
|
+
["VERSION", "onkyo_eiscp_ruby.gemspec", "eiscp-commands.yaml"]
|
8
|
+
s.extra_rdoc_files = ["README.md"]
|
9
|
+
s.require_path = 'lib'
|
10
|
+
|
11
|
+
s.homepage = "https://github.com/mikerodrigues/onkyo_eiscp_ruby"
|
12
|
+
|
13
|
+
s.description = %q(
|
14
|
+
Use the provided binary script or require the library for use in your scripts.
|
15
|
+
)
|
16
|
+
|
17
|
+
s.author = "Michael Rodrigues"
|
18
|
+
s.email = "mikebrodrigues@gmail.com"
|
19
|
+
|
20
|
+
s.test_files = Dir[ 'test/tc*.rb' ]
|
21
|
+
s.executables = %w(
|
22
|
+
onkyo.rb
|
23
|
+
)
|
24
|
+
|
25
|
+
end
|
data/test/tc_command.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative "../lib/eiscp/command.rb"
|
2
|
+
require "test/unit"
|
3
|
+
|
4
|
+
class TestCommand < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_command_to_name
|
7
|
+
assert_equal(Command.command_to_name("PWR"), "system-power")
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_name_to_command
|
11
|
+
assert_equal(Command.name_to_command("system-power"), "PWR")
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_command_value_to_value_name
|
15
|
+
assert_equal(Command.command_value_to_value_name("PWR", "01"), "on")
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_command_value_name_to_value
|
19
|
+
assert_equal(Command.command_value_name_to_value("PWR", "on"), "01")
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_description_from_name
|
23
|
+
assert_equal(Command.description_from_name("system-power"), "System Power Command")
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_description_from_command
|
27
|
+
assert_equal(Command.description_from_command("PWR"), "System Power Command")
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/test/tc_eiscp.rb
ADDED
@@ -0,0 +1,17 @@
|
|
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
|
@@ -0,0 +1,14 @@
|
|
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
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: onkyo_eiscp_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Michael Rodrigues
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-01 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! "\n Use the provided binary script or require the library for use
|
15
|
+
in your scripts.\n "
|
16
|
+
email: mikebrodrigues@gmail.com
|
17
|
+
executables:
|
18
|
+
- onkyo.rb
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files:
|
21
|
+
- README.md
|
22
|
+
files:
|
23
|
+
- bin/onkyo.rb
|
24
|
+
- lib/eiscp/eiscp.rb
|
25
|
+
- lib/eiscp/eiscp_packet.rb
|
26
|
+
- lib/eiscp/command.rb
|
27
|
+
- lib/eiscp/iscp_message.rb
|
28
|
+
- lib/eiscp.rb
|
29
|
+
- lib/server.rb
|
30
|
+
- test/tc_command.rb
|
31
|
+
- test/tc_iscp_message.rb
|
32
|
+
- test/tc_eiscp.rb
|
33
|
+
- test/tc_eiscp_packet.rb
|
34
|
+
- VERSION
|
35
|
+
- onkyo_eiscp_ruby.gemspec
|
36
|
+
- eiscp-commands.yaml
|
37
|
+
- README.md
|
38
|
+
homepage: https://github.com/mikerodrigues/onkyo_eiscp_ruby
|
39
|
+
licenses: []
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.8.25
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Manipulate Onkyo stereos with the eISCP protocol
|
62
|
+
test_files:
|
63
|
+
- test/tc_command.rb
|
64
|
+
- test/tc_iscp_message.rb
|
65
|
+
- test/tc_eiscp.rb
|
66
|
+
- test/tc_eiscp_packet.rb
|
67
|
+
has_rdoc:
|