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.
@@ -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
@@ -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
- ["VERSION", "onkyo_eiscp_ruby.gemspec", "eiscp-commands.yaml"]
8
- s.extra_rdoc_files = ["README.md"]
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 = "https://github.com/mikerodrigues/onkyo_eiscp_ruby"
14
+ s.homepage = 'https://github.com/mikerodrigues/onkyo_eiscp_ruby'
12
15
 
13
- s.description = %q(
14
- Control Onkyo receivers over the network.Use the provided binary script or
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 = "Michael Rodrigues"
19
- s.email = "mikebrodrigues@gmail.com"
21
+ s.author = 'Michael Rodrigues'
22
+ s.email = 'mikebrodrigues@gmail.com'
20
23
 
21
- s.test_files = Dir[ 'test/tc*.rb' ]
22
- s.executables = %w(
24
+ s.test_files = Dir['test/tc*.rb']
25
+ s.executables = %w[
23
26
  onkyo.rb
24
- onkyo-server.rb
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
- require_relative "../lib/eiscp/message"
2
- require "test/unit"
1
+ # frozen_string_literal: true
3
2
 
4
- class TestMessage < Test::Unit::TestCase
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("ECN", "QSTN", "x", "!").to_iscp, "!xECNQSTN")
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 test_parse_discovery_iscp_message
16
- assert_equal(EISCP::Message.parse("!xECNQSTN").to_iscp, "!xECNQSTN")
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 test_parse_discovery_packet_string
24
- assert_equal(EISCP::Message.parse(DISCOVERY_STRING).to_eiscp, DISCOVERY_PACKET.to_eiscp)
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
@@ -1,6 +1,7 @@
1
- require_relative "../lib/eiscp/receiver"
2
- require "test/unit"
1
+ # frozen_string_literal: true
3
2
 
4
- class TestEISCP
3
+ require_relative '../lib/eiscp/receiver'
4
+ require 'minitest/autorun'
5
5
 
6
+ class TestEISCP
6
7
  end
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: 0.0.3
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: 2014-01-20 00:00:00.000000000 Z
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
- script or\n require the library for use in your scripts.\n "
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
- - onkyo-server.rb
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/onkyo-server.rb
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/command.rb
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/mock_receiver.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
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/tc_command.rb
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
- rubyforge_project:
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
-
@@ -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