onkyo_eiscp_ruby 0.0.2 → 2.1.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.
- checksums.yaml +7 -0
- data/README.md +213 -29
- data/VERSION +1 -1
- data/bin/mock_receiver.rb +25 -0
- data/bin/onkyo.rb +85 -23
- data/bin/onkyo_server.rb +8 -0
- data/eiscp-commands.yaml +3911 -3900
- data/lib/eiscp.rb +9 -7
- data/lib/eiscp/dictionary.rb +54 -0
- data/lib/eiscp/dictionary/dictionary_generators.rb +63 -0
- data/lib/eiscp/dictionary/dictionary_helpers.rb +116 -0
- data/lib/eiscp/message.rb +123 -0
- data/lib/eiscp/parser.rb +24 -0
- data/lib/eiscp/parser/dynamic_value_parser.rb +9 -0
- data/lib/eiscp/parser/eiscp_parser.rb +37 -0
- data/lib/eiscp/parser/human_readable_parser.rb +29 -0
- data/lib/eiscp/parser/iscp_parser.rb +28 -0
- data/lib/eiscp/receiver.rb +203 -0
- data/lib/eiscp/receiver/command_methods.rb +28 -0
- data/lib/eiscp/receiver/discovery.rb +49 -0
- data/onkyo_eiscp_ruby.gemspec +16 -13
- data/test/tc_dictionary.rb +45 -0
- data/test/tc_message.rb +26 -0
- data/test/tc_parser.rb +34 -0
- data/test/tc_receiver.rb +7 -0
- metadata +37 -32
- data/bin/onkyo-server.rb +0 -7
- data/lib/eiscp/command.rb +0 -80
- data/lib/eiscp/eiscp.rb +0 -125
- data/lib/eiscp/eiscp_packet.rb +0 -51
- data/lib/eiscp/eiscp_server.rb +0 -21
- data/lib/eiscp/iscp_message.rb +0 -24
- data/test/tc_command.rb +0 -30
- data/test/tc_eiscp.rb +0 -6
- data/test/tc_eiscp_packet.rb +0 -17
- data/test/tc_iscp_message.rb +0 -14
data/test/tc_parser.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/eiscp/parser'
|
4
|
+
require_relative '../lib/eiscp/message'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
|
7
|
+
class TestParser < MiniTest::Test
|
8
|
+
DISCOVERY_PACKET = EISCP::Message.new(command: 'ECN', value: 'QSTN', terminator: "\r\n", unit_type: 'x', start: '!')
|
9
|
+
DISCOVERY_STRING = DISCOVERY_PACKET.to_eiscp
|
10
|
+
|
11
|
+
def test_parse_discovery_iscp_message
|
12
|
+
assert_equal(EISCP::Parser.parse('!xECNQSTN').to_iscp, '!xECNQSTN')
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_parse_iscp_messages
|
16
|
+
assert_equal(EISCP::Parser.parse('PWR 01').to_iscp, '!1PWR01')
|
17
|
+
assert_equal(EISCP::Parser.parse('PWR01').to_iscp, '!1PWR01')
|
18
|
+
assert_equal(EISCP::Parser.parse('!1PWR01').to_iscp, '!1PWR01')
|
19
|
+
assert_equal(EISCP::Parser.parse('!1PWR 01').to_iscp, '!1PWR01')
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_parse_discovery_packet_string
|
23
|
+
assert_equal(EISCP::Parser.parse(DISCOVERY_STRING).to_eiscp, DISCOVERY_PACKET.to_eiscp)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_parse_human_readable
|
27
|
+
assert_equal(EISCP::Parser.parse('system-power on'), EISCP::Message.new(command: 'PWR', value: '01'))
|
28
|
+
assert_equal(EISCP::Parser.parse('main system-power on'), EISCP::Message.new(command: 'PWR', value: '01'))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_return_nil_for_fake_human_readable
|
32
|
+
assert_equal(EISCP::Parser.parse('fake-command value'), nil)
|
33
|
+
end
|
34
|
+
end
|
data/test/tc_receiver.rb
ADDED
metadata
CHANGED
@@ -1,69 +1,74 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: onkyo_eiscp_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.1.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Michael Rodrigues
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2021-03-21 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
|
-
description:
|
15
|
-
in your scripts.\n "
|
13
|
+
description: "\n Control Onkyo receivers over the network.Use the provided binary
|
14
|
+
or\n require the library for use in your scripts.\n "
|
16
15
|
email: mikebrodrigues@gmail.com
|
17
16
|
executables:
|
18
17
|
- onkyo.rb
|
19
|
-
-
|
18
|
+
- onkyo_server.rb
|
20
19
|
extensions: []
|
21
20
|
extra_rdoc_files:
|
22
21
|
- README.md
|
23
22
|
files:
|
24
|
-
-
|
23
|
+
- README.md
|
24
|
+
- VERSION
|
25
|
+
- bin/mock_receiver.rb
|
25
26
|
- bin/onkyo.rb
|
26
|
-
-
|
27
|
-
-
|
28
|
-
- lib/eiscp/eiscp_packet.rb
|
29
|
-
- lib/eiscp/command.rb
|
30
|
-
- lib/eiscp/iscp_message.rb
|
27
|
+
- bin/onkyo_server.rb
|
28
|
+
- eiscp-commands.yaml
|
31
29
|
- lib/eiscp.rb
|
32
|
-
-
|
33
|
-
-
|
34
|
-
-
|
35
|
-
-
|
36
|
-
-
|
30
|
+
- lib/eiscp/dictionary.rb
|
31
|
+
- lib/eiscp/dictionary/dictionary_generators.rb
|
32
|
+
- lib/eiscp/dictionary/dictionary_helpers.rb
|
33
|
+
- lib/eiscp/message.rb
|
34
|
+
- lib/eiscp/parser.rb
|
35
|
+
- lib/eiscp/parser/dynamic_value_parser.rb
|
36
|
+
- lib/eiscp/parser/eiscp_parser.rb
|
37
|
+
- lib/eiscp/parser/human_readable_parser.rb
|
38
|
+
- lib/eiscp/parser/iscp_parser.rb
|
39
|
+
- lib/eiscp/receiver.rb
|
40
|
+
- lib/eiscp/receiver/command_methods.rb
|
41
|
+
- lib/eiscp/receiver/discovery.rb
|
37
42
|
- onkyo_eiscp_ruby.gemspec
|
38
|
-
-
|
39
|
-
-
|
43
|
+
- test/tc_dictionary.rb
|
44
|
+
- test/tc_message.rb
|
45
|
+
- test/tc_parser.rb
|
46
|
+
- test/tc_receiver.rb
|
40
47
|
homepage: https://github.com/mikerodrigues/onkyo_eiscp_ruby
|
41
|
-
licenses:
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
metadata: {}
|
42
51
|
post_install_message:
|
43
52
|
rdoc_options: []
|
44
53
|
require_paths:
|
45
54
|
- lib
|
46
55
|
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
-
none: false
|
48
56
|
requirements:
|
49
|
-
- -
|
57
|
+
- - ">="
|
50
58
|
- !ruby/object:Gem::Version
|
51
59
|
version: '0'
|
52
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
-
none: false
|
54
61
|
requirements:
|
55
|
-
- -
|
62
|
+
- - ">="
|
56
63
|
- !ruby/object:Gem::Version
|
57
64
|
version: '0'
|
58
65
|
requirements: []
|
59
|
-
|
60
|
-
rubygems_version: 1.8.25
|
66
|
+
rubygems_version: 3.2.3
|
61
67
|
signing_key:
|
62
|
-
specification_version:
|
68
|
+
specification_version: 4
|
63
69
|
summary: Manipulate Onkyo stereos with the eISCP protocol
|
64
70
|
test_files:
|
65
|
-
- test/
|
66
|
-
- test/
|
67
|
-
- test/
|
68
|
-
- test/
|
69
|
-
has_rdoc:
|
71
|
+
- test/tc_dictionary.rb
|
72
|
+
- test/tc_message.rb
|
73
|
+
- test/tc_parser.rb
|
74
|
+
- test/tc_receiver.rb
|
data/bin/onkyo-server.rb
DELETED
data/lib/eiscp/command.rb
DELETED
@@ -1,80 +0,0 @@
|
|
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
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
|
-
|
data/lib/eiscp/eiscp_packet.rb
DELETED
@@ -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
|
-
|
data/lib/eiscp/eiscp_server.rb
DELETED
@@ -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
|
-
|
data/lib/eiscp/iscp_message.rb
DELETED
@@ -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
|