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.
@@ -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: