onkyo_eiscp_ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
@@ -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
@@ -0,0 +1,9 @@
1
+ # Public: Create and send EISCP messages to control Onkyo receivers.
2
+ class EISCP
3
+ VERSION = '0.0.1'
4
+ end
5
+
6
+ require 'eiscp/eiscp'
7
+ require 'eiscp/eiscp_packet'
8
+ require 'eiscp/iscp_message'
9
+ require 'eiscp/command'
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
@@ -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,6 @@
1
+ require_relative "../lib/eiscp/eiscp.rb"
2
+ require "test/unit"
3
+
4
+ class TestEISCP
5
+
6
+ end
@@ -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: