onkyo_eiscp_ruby 0.0.3 → 2.1.2
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 +5 -5
- data/README.md +182 -32
- data/VERSION +1 -1
- data/bin/mock_receiver.rb +25 -0
- data/bin/onkyo.rb +85 -25
- data/bin/{onkyo-server.rb → onkyo_server.rb} +2 -1
- data/eiscp-commands.yaml +3911 -3900
- data/lib/eiscp.rb +8 -5
- data/lib/eiscp/dictionary.rb +54 -0
- data/lib/eiscp/dictionary/dictionary_generators.rb +63 -0
- data/lib/eiscp/dictionary/dictionary_helpers.rb +121 -0
- data/lib/eiscp/message.rb +98 -74
- 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 +158 -135
- data/lib/eiscp/receiver/command_methods.rb +28 -0
- data/lib/eiscp/receiver/discovery.rb +49 -0
- data/onkyo_eiscp_ruby.gemspec +15 -13
- data/test/tc_dictionary.rb +45 -0
- data/test/tc_message.rb +11 -12
- data/test/tc_parser.rb +34 -0
- data/test/tc_receiver.rb +4 -3
- metadata +23 -12
- data/lib/eiscp/command.rb +0 -99
- data/lib/eiscp/mock_receiver.rb +0 -22
- data/test/tc_command.rb +0 -43
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../parser'
|
4
|
+
require_relative '../dictionary'
|
5
|
+
|
6
|
+
module EISCP
|
7
|
+
class Receiver
|
8
|
+
# Iterates through every available command and defines a method to call that
|
9
|
+
# command. It's intended to be used through Receiver and uses methods included
|
10
|
+
# by Receiver::Connection. Each method accepts a string that should match the
|
11
|
+
# human readable name of a valid value for that command.
|
12
|
+
#
|
13
|
+
module CommandMethods
|
14
|
+
def self.generate(&block)
|
15
|
+
Dictionary.zones.each do |zone|
|
16
|
+
Dictionary.commands[zone].each do |command, _values|
|
17
|
+
command_name = Dictionary.command_to_name(command).to_s.gsub(/-/, '_')
|
18
|
+
define_method(command_name) do |v|
|
19
|
+
instance_exec Parser.parse(command_name.gsub(/_/, '-') + ' ' + v), &block
|
20
|
+
end
|
21
|
+
rescue StandardError => e
|
22
|
+
puts e
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
require_relative '../message'
|
5
|
+
require_relative '../parser'
|
6
|
+
|
7
|
+
module EISCP
|
8
|
+
class Receiver
|
9
|
+
# This module discovers receivers on the local LAN.
|
10
|
+
#
|
11
|
+
module Discovery
|
12
|
+
# ISCP Magic Packet for Autodiscovery
|
13
|
+
ONKYO_MAGIC = Message.new(command: 'ECN', value: 'QSTN', terminator: "\r\n", unit_type: 'x').to_eiscp
|
14
|
+
|
15
|
+
# Populates Receiver attributes with info from ECNQSTN response.
|
16
|
+
#
|
17
|
+
def ecn_string_to_ecn_array(ecn_string)
|
18
|
+
hash = {}
|
19
|
+
message = Parser.parse(ecn_string)
|
20
|
+
array = message.value.split('/')
|
21
|
+
hash[:model] = array.shift
|
22
|
+
hash[:port] = array.shift.to_i
|
23
|
+
hash[:area] = array.shift
|
24
|
+
hash[:mac_address] = array.shift
|
25
|
+
hash
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns an array of discovered Receiver objects.
|
29
|
+
#
|
30
|
+
def discover(discovery_port = Receiver::ONKYO_PORT)
|
31
|
+
sock = UDPSocket.new
|
32
|
+
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
|
33
|
+
sock.send(ONKYO_MAGIC, 0, '<broadcast>', discovery_port)
|
34
|
+
data = []
|
35
|
+
loop do
|
36
|
+
msg, addr = sock.recvfrom_nonblock(1024)
|
37
|
+
data << Receiver.new(addr[2], ecn_string_to_ecn_array(msg))
|
38
|
+
rescue IO::WaitReadable
|
39
|
+
io = IO.select([sock], nil, nil, 0.5)
|
40
|
+
if io.nil?
|
41
|
+
return data
|
42
|
+
else
|
43
|
+
retry
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/onkyo_eiscp_ruby.gemspec
CHANGED
@@ -1,27 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Gem::Specification.new do |s|
|
2
4
|
s.name = 'onkyo_eiscp_ruby'
|
3
5
|
s.version = File.read(File.expand_path('VERSION', File.dirname(__FILE__))).strip
|
6
|
+
s.licenses = ['MIT']
|
4
7
|
s.platform = Gem::Platform::RUBY
|
5
8
|
s.summary = 'Manipulate Onkyo stereos with the eISCP protocol'
|
6
9
|
s.files = Dir.glob('{bin,config,lib,test,doc}/**/*') +
|
7
|
-
|
8
|
-
s.extra_rdoc_files = [
|
10
|
+
['VERSION', 'onkyo_eiscp_ruby.gemspec', 'eiscp-commands.yaml']
|
11
|
+
s.extra_rdoc_files = ['README.md']
|
9
12
|
s.require_path = 'lib'
|
10
13
|
|
11
|
-
s.homepage =
|
14
|
+
s.homepage = 'https://github.com/mikerodrigues/onkyo_eiscp_ruby'
|
12
15
|
|
13
|
-
s.description =
|
14
|
-
Control Onkyo receivers over the network.Use the provided binary
|
16
|
+
s.description = '
|
17
|
+
Control Onkyo receivers over the network.Use the provided binary or
|
15
18
|
require the library for use in your scripts.
|
16
|
-
|
19
|
+
'
|
17
20
|
|
18
|
-
s.author =
|
19
|
-
s.email =
|
21
|
+
s.author = 'Michael Rodrigues'
|
22
|
+
s.email = 'mikebrodrigues@gmail.com'
|
20
23
|
|
21
|
-
s.test_files = Dir[
|
22
|
-
s.executables = %w
|
24
|
+
s.test_files = Dir['test/tc*.rb']
|
25
|
+
s.executables = %w[
|
23
26
|
onkyo.rb
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
onkyo_server.rb
|
28
|
+
]
|
27
29
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../lib/eiscp/dictionary.rb'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
class TestDictionary < MiniTest::Test
|
7
|
+
def test_zone_from_command
|
8
|
+
assert_equal(EISCP::Dictionary.zone_from_command('PWR'), 'main')
|
9
|
+
assert_equal(EISCP::Dictionary.zone_from_command('ZPW'), 'zone2')
|
10
|
+
assert_equal(EISCP::Dictionary.zone_from_command('CDS'), 'dock')
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_command_to_name
|
14
|
+
assert_equal(EISCP::Dictionary.command_to_name('PWR'), 'system-power')
|
15
|
+
assert_equal(EISCP::Dictionary.command_to_name('ZPW'), 'power2')
|
16
|
+
assert_equal(EISCP::Dictionary.command_to_name('PW3'), 'power3')
|
17
|
+
assert_equal(EISCP::Dictionary.command_to_name('PW4'), 'power4')
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_command_name_to_command
|
21
|
+
assert_equal(EISCP::Dictionary.command_name_to_command('system-power'), 'PWR')
|
22
|
+
assert_equal(EISCP::Dictionary.command_name_to_command('master-volume'), 'MVL')
|
23
|
+
assert_equal(EISCP::Dictionary.command_name_to_command('power2'), 'ZPW')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_command_value_to_value_name
|
27
|
+
assert_equal(EISCP::Dictionary.command_value_to_value_name('PWR', '01'), 'on')
|
28
|
+
assert_equal(EISCP::Dictionary.command_value_to_value_name('PWR', 'QSTN'), 'query')
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_command_value_name_to_value
|
32
|
+
assert_equal(EISCP::Dictionary.command_value_name_to_value('PWR', 'on'), '01')
|
33
|
+
assert_equal(EISCP::Dictionary.command_value_name_to_value('ZPW', 'on'), '01')
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_description_from_command_name
|
37
|
+
assert_equal(EISCP::Dictionary.description_from_command_name('system-power', 'main'), 'System Power Command')
|
38
|
+
assert_equal(EISCP::Dictionary.description_from_command_name('power2', 'zone2'), 'Zone2 Power Command')
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_description_from_command
|
42
|
+
assert_equal(EISCP::Dictionary.description_from_command('PWR'), 'System Power Command')
|
43
|
+
assert_equal(EISCP::Dictionary.description_from_command('ZPW'), 'Zone2 Power Command')
|
44
|
+
end
|
45
|
+
end
|
data/test/tc_message.rb
CHANGED
@@ -1,27 +1,26 @@
|
|
1
|
-
|
2
|
-
require "test/unit"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
3
|
+
require_relative '../lib/eiscp/message'
|
4
|
+
require 'minitest/autorun'
|
5
5
|
|
6
|
-
|
7
|
-
DISCOVERY_PACKET = EISCP::Message.new('ECN', 'QSTN', 'x', '!')
|
6
|
+
class TestMessage < MiniTest::Test
|
7
|
+
DISCOVERY_PACKET = EISCP::Message.new(command: 'ECN', value: 'QSTN', terminator: "\r\n", unit_type: 'x', start: '!')
|
8
8
|
DISCOVERY_STRING = DISCOVERY_PACKET.to_eiscp
|
9
9
|
|
10
|
-
|
11
10
|
def test_create_discovery_iscp_message
|
12
|
-
assert_equal(EISCP::Message.new(
|
11
|
+
assert_equal(EISCP::Message.new(command: 'ECN', value: 'QSTN', terminator: "\r\n", unit_type: 'x', start: '!').to_iscp, '!xECNQSTN')
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
16
|
-
assert_equal(EISCP::Message.
|
14
|
+
def test_create_messages
|
15
|
+
assert_equal(EISCP::Message.new(command: 'PWR', value: '01').to_iscp, '!1PWR01')
|
16
|
+
assert_equal(EISCP::Message.new(command: 'MVL', value: 'QSTN').to_iscp, '!1MVLQSTN')
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_create_discovery_packet_string
|
20
20
|
assert_equal(DISCOVERY_PACKET.to_eiscp, DISCOVERY_STRING)
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
|
23
|
+
def test_validate_valid_message_with_variable
|
24
|
+
# Commands that return something unexpected like an artist name
|
25
25
|
end
|
26
|
-
|
27
26
|
end
|
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
CHANGED
metadata
CHANGED
@@ -1,41 +1,52 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: onkyo_eiscp_ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Rodrigues
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: "\n Control Onkyo receivers over the network.Use the provided binary
|
14
|
-
|
14
|
+
or\n require the library for use in your scripts.\n "
|
15
15
|
email: mikebrodrigues@gmail.com
|
16
16
|
executables:
|
17
17
|
- onkyo.rb
|
18
|
-
-
|
18
|
+
- onkyo_server.rb
|
19
19
|
extensions: []
|
20
20
|
extra_rdoc_files:
|
21
21
|
- README.md
|
22
22
|
files:
|
23
23
|
- README.md
|
24
24
|
- VERSION
|
25
|
-
- bin/
|
25
|
+
- bin/mock_receiver.rb
|
26
26
|
- bin/onkyo.rb
|
27
|
+
- bin/onkyo_server.rb
|
27
28
|
- eiscp-commands.yaml
|
28
29
|
- lib/eiscp.rb
|
29
|
-
- lib/eiscp/
|
30
|
+
- lib/eiscp/dictionary.rb
|
31
|
+
- lib/eiscp/dictionary/dictionary_generators.rb
|
32
|
+
- lib/eiscp/dictionary/dictionary_helpers.rb
|
30
33
|
- lib/eiscp/message.rb
|
31
|
-
- lib/eiscp/
|
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
|
32
39
|
- lib/eiscp/receiver.rb
|
40
|
+
- lib/eiscp/receiver/command_methods.rb
|
41
|
+
- lib/eiscp/receiver/discovery.rb
|
33
42
|
- onkyo_eiscp_ruby.gemspec
|
34
|
-
- test/
|
43
|
+
- test/tc_dictionary.rb
|
35
44
|
- test/tc_message.rb
|
45
|
+
- test/tc_parser.rb
|
36
46
|
- test/tc_receiver.rb
|
37
47
|
homepage: https://github.com/mikerodrigues/onkyo_eiscp_ruby
|
38
|
-
licenses:
|
48
|
+
licenses:
|
49
|
+
- MIT
|
39
50
|
metadata: {}
|
40
51
|
post_install_message:
|
41
52
|
rdoc_options: []
|
@@ -52,12 +63,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
63
|
- !ruby/object:Gem::Version
|
53
64
|
version: '0'
|
54
65
|
requirements: []
|
55
|
-
|
56
|
-
rubygems_version: 2.2.0
|
66
|
+
rubygems_version: 3.2.3
|
57
67
|
signing_key:
|
58
68
|
specification_version: 4
|
59
69
|
summary: Manipulate Onkyo stereos with the eISCP protocol
|
60
70
|
test_files:
|
71
|
+
- test/tc_dictionary.rb
|
61
72
|
- test/tc_message.rb
|
73
|
+
- test/tc_parser.rb
|
62
74
|
- test/tc_receiver.rb
|
63
|
-
- test/tc_command.rb
|
data/lib/eiscp/command.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'eiscp/receiver'
|
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
|
-
def self.command_to_name(command)
|
16
|
-
return @@main[command]['name']
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.command_name_to_command(name)
|
20
|
-
@@main.each_pair do |command, attrs|
|
21
|
-
if attrs['name'] == name
|
22
|
-
return command
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.command_value_to_value_name(command, value)
|
28
|
-
return @@main[command]['values'][value]['name']
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.command_value_name_to_value(command, name)
|
32
|
-
@@main[command]['values'].each do |k, v|
|
33
|
-
if v['name'] == name.to_s
|
34
|
-
return k
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
def self.description_from_command_name(name)
|
41
|
-
@@main.each_pair do |command, attrs|
|
42
|
-
if attrs['name'] == name
|
43
|
-
return @@main[command]['description']
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.description_from_command(command)
|
49
|
-
return @@main[command]['description']
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.description_from_command_value(command, value)
|
53
|
-
return @@main[command]['values'].select do |k, v|
|
54
|
-
if k == value
|
55
|
-
return v['description']
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.list_all_commands
|
61
|
-
@@main.each_pair do |command, attrs|
|
62
|
-
puts "#{command} - #{attrs['name']}: #{attrs['description']}"
|
63
|
-
attrs['values'].each_pair do |k, v|
|
64
|
-
puts "--#{k}:#{v}"
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
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
|
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)
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
|
data/lib/eiscp/mock_receiver.rb
DELETED
@@ -1,22 +0,0 @@
|
|
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
|