onkyo_eiscp_ruby 0.0.3 → 1.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be075bcd7a1830e7614b8384d610ea59d0b7160f
4
- data.tar.gz: 40cea299c77c937a786f387cfbf4c11e8696632c
3
+ metadata.gz: ab3d51dea59ba8c99a8782be06c6508cd3b37163
4
+ data.tar.gz: aa346aaa381eda5418f7d7233b475d9f54e04b51
5
5
  SHA512:
6
- metadata.gz: 5066d43a66350cb39e84ddbae87038d4ba04057f2dd884eee1852c3dd108ed4a44842878157dfb854e7802d3e771cbe5789a70af936af0e338a36264e92332d6
7
- data.tar.gz: 6c2c8bb4ba7d3b7e9fee332d448a054cf97480194192252965b363b7efaa8d8b095ae76d2f68ab6f064fbf68ec2bac75c63b2ba53a26e891b055c0751f776cc4
6
+ metadata.gz: 0c75784e4c01fe2ad2031933f5ea5f6204ccf81b543985a93dd2d301dac5e714aff529508d0fc3545013b7723fb84043f0405903e4444b8ca61635dab0ccdde4
7
+ data.tar.gz: 07aba89079c12e05b5eac6d4d475f7818568744d16d9c3196e714efff07b21f8c86308afdd8a6953a9b97045a26a146ca9dbac18f58e03a216368cc78ffdb293
data/README.md CHANGED
@@ -1,21 +1,40 @@
1
1
  onkyo_eiscp_ruby
2
2
  ================
3
3
  [![Gem Version](https://badge.fury.io/rb/onkyo_eiscp_ruby.png)](http://badge.fury.io/rb/onkyo_eiscp_ruby)
4
+ [![GitHub version](https://badge.fury.io/gh/mikerodrigues%2Fonkyo_eiscp_ruby.png)](http://badge.fury.io/gh/mikerodrigues%2Fonkyo_eiscp_ruby)
4
5
 
5
- A Ruby implementation of eISCP for controlling Onkyo receivers.
6
+ *A Ruby implementation of eISCP for controlling Onkyo receivers.*
6
7
 
7
8
  **This code is still under heavy development and using it might make you sick.**
8
- Create ISCP messages and eISCP packets
9
- Automatically discover receiver's in the broadcast domain
10
- Send/Recieve eISCP messages
11
- Open a TCP socket to send commands and receive solicited and non-solicited status updates.
9
+
10
+ Automatically discover receivers in the broadcast domain
11
+
12
+ Send commands to receivers and parse returned messages
13
+
14
+ Open a TCP socket to receive solicited and non-solicited status updates.
15
+
12
16
  Mock reciever (currently only responds to discovery)
13
17
 
18
+ Human-readable commands
19
+
14
20
  **Inspired by https://github.com/miracle2k/onkyo-eiscp
15
21
 
16
22
  **Protocol information from http://michael.elsdoerfer.name/onkyo/ISCP-V1.21_2011.xls
17
23
 
24
+ What's missing?
25
+ ---------------
26
+ * Command validation
27
+
28
+ * Parsing of all human readable commands (run the tests to see some commands that aren't parsable in human readable form yet.
18
29
 
30
+ * Reasonable variants for human-readable commands (ex. `main-volume` or`volume
31
+ ` as opposed to `master-volume`)
32
+
33
+ * Model compatability checking
34
+
35
+ * Logging
36
+
37
+ * Exhaustive testing and documentation
19
38
 
20
39
  Using the Library
21
40
  -----------------
@@ -23,53 +42,155 @@ Using the Library
23
42
 
24
43
  require 'eiscp'
25
44
 
26
- * Discover local receivers
27
-
45
+ * You might want to `include EISCP` if you know you won't pollute your namespace
46
+ with Constants under `EISCP` (`Dictionary`, `Message`, `Parser`, `Receiver`,
47
+ `VERSION`)
48
+
49
+ * You can do most everything through the `Receiver` and `Message` objects. If you
50
+ want to accept user input you will probably want to use the Parser module. Be
51
+ sure to check out the RDocs or dig through the source code. I try to keep it
52
+ well commented/documented, and there's more functionality to the library than
53
+ is shown here:
54
+
55
+ * The `Message` object is pretty self explanatory. `Message.new` is mostly used
56
+ internally, but you're better of using `Parser.parse` to create them. You
57
+ probably will want to interact with `Message` objects to get information:
58
+
59
+ ```ruby
60
+ msg = EISCP::Message.new(command: 'PWR', value: '01')
61
+ msg.zone => 'main'
62
+ msg.command => "PWR"
63
+ msg.value => "01"
64
+ msg.command_name => "system-power"
65
+ msg.command_description => "System Power Command"
66
+ msg.value_name => "on"
67
+ msg.value_description => "sets System On"
68
+ ```
69
+
70
+ * Discover local receivers (returns an `Array` of `Receiver` objects)
71
+
72
+ ```ruby
28
73
  EISCP::Receiver.discover
74
+ ```
29
75
 
30
- * Create Receiver object from first discovered
76
+ * Create `Receiver` object from first discovered Receiver on the LAN
31
77
 
32
- Receiver.new
78
+ ```ruby
79
+ receiver = EISCP::Receiver.new
80
+ ```
33
81
 
34
- * Open a TCP connection to monitor solicited updates
82
+ * Or create one manually by IP address or hostname
35
83
 
36
- receiver = Receiver.new('10.0.0.1')
37
- receiver.connect
84
+ ```ruby
85
+ receiver = EISCP::Receiver.new('10.0.0.132')
86
+ ```
38
87
 
39
- * You can also pass a block and operate on received packet strings:
88
+ * When you create a `Receiver` object, it uses the `Receiver::Connection` module to
89
+ make a connection and monitor incoming messages. By default, the last message
90
+ received can be retrieved with `receiver.last`. You can
91
+ pass your own block at creation time, it will have access to messages as they
92
+ come in. This will let you setup callbacks to run when messages are receivedL
40
93
 
41
- receiver.connect do |data|
42
- puts EISCP::Receiver.parse(data).iscp_message
94
+ ```ruby
95
+ receiver = EISCP::Receiver.new do |msg|
96
+ puts msg.command
97
+ puts msg.value
43
98
  end
99
+ ```
44
100
 
45
- * Turn on the receiver
101
+ * You can also change the block later. This will kill the existing connection
102
+ thread (but not the socket) and start your new one:
46
103
 
47
- message = EISCP::Message.parse("PWR", "01")
48
- message.send(message.to_eiscp)
49
-
50
- * New 'parse' method makes creating EISCP objects more flexible.
51
- This parses messages from command line or raw eiscp data from the socket
52
-
53
- iscp_message = EISCP::Message.parse "PWR01"
54
- iscp_message = EISCP::Message.parse "PWR 01"
55
- iscp_message = EISCP::Message.parse "!1PWR01"
56
- iscp_message = EISCP::Message.parse "!1PWR 01"
57
-
58
- * Parsing raw socket data
104
+ ```ruby
105
+ receiver.update_thread do |msg|
106
+ puts "Received: #{msg.command_name}:#{msg.value_name}"
107
+ end
108
+ ```
109
+
110
+ * Get information about the Receiver:
111
+
112
+ ```ruby
113
+ receiver.model => "TX-NR609"
114
+ receiver.host => "10.0.0.111"
115
+ receiver.port => 60128
116
+ receiver.mac_address => "001122334455"
117
+ receiver.area => "DX"
118
+ ```
119
+
120
+ * Get the last message received from the Receiver:
121
+
122
+ ```ruby
123
+ receiver.last
124
+ ```
125
+
126
+ * You can use `CommandMethods` to easily send a message and return the reply as
127
+ a Message object. A method is defined for each command listed in the
128
+ `Dictionary` using the `@command_name` attribute which is 'human readable'.
129
+ You can check the included yaml file or look at the output of
130
+ `EISCP::Dictionary.commands`. Here a few examples:
131
+
132
+ ```ruby
133
+ # Turn on receiver
134
+ receiver.system_power "on"
135
+
136
+ # Query current input source
137
+ receiver.input_selector "query"
138
+
139
+ # Turn the master volume up one level
140
+ receiver.master_volume "level-up"
141
+
142
+ # Set the master volume to 45
143
+ receiver.master_volume "45"
144
+ ```
145
+
146
+ * Parse ISCP and human readable strings:
147
+
148
+ ```ruby
149
+ # Parse various ISCP strings
150
+ iscp_message = EISCP::Parser.parse "PWR01"
151
+ iscp_message = EISCP::Parser.parse "PWR 01"
152
+ iscp_message = EISCP::Parser.parse "!1PWR01"
153
+ iscp_message = EISCP::Parser.parse "!1PWR 01"
154
+
155
+ # Parse human readable,
156
+ EISCP::Parser.parse("main-volume 34")
157
+ ```
158
+
159
+ * `Parser.parse` is also used internally by `Receiver` to parse raw eISCP socket
160
+ data.
59
161
 
60
- iscp_message_from_raw_eiscp = EISCP::Message.parse iscp_message.to_eiscp
61
162
 
62
163
  Using the Binaries
63
164
  ------------------
64
165
 
65
166
  * Discover local receivers
66
167
 
67
- $ onkyo.rb -d
168
+ `$ onkyo.rb -d`
169
+
170
+ * Send a human-readable command
171
+
172
+ `$ onkyo.rb system-power on # uses Command.parse`
173
+
174
+ * Or send a raw command
68
175
 
69
- * Connect to the first discovered receiver to see status updates
176
+ `$ onkyo.rb PWRQSTN # Also tries to use Message.parse`
70
177
 
71
- $ onkyo.rb -c
178
+ * Monitor the first discovered receiver to see status updates
179
+
180
+ `$ onkyo.rb -m`
72
181
 
73
182
  * Start the mock server (only responds to 'ECNQSTN')
74
183
 
75
- $ onkyo-server.rb
184
+ `$ onkyo-server.rb`
185
+
186
+ * Turn off the first receiver discovered:
187
+
188
+ `$ onkyo.rb system-power off`
189
+
190
+ Contributing
191
+ ------------
192
+
193
+ * Open an issue describing bug or feature
194
+ * Fork repo
195
+ * Create a branch
196
+ * Send pull request
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 1.0.4
@@ -0,0 +1,23 @@
1
+ require 'socket'
2
+ require_relative './receiver'
3
+ require_relative './message'
4
+
5
+ # Mock server that only responds to ECNQSTN.
6
+
7
+ module EISCP
8
+ # This class acts as an Onkyo Stereo. It can be used for testing
9
+ #
10
+ class MockReceiver
11
+ DISCOVERY_IP = '255.255.255.255'
12
+ ONKYO_DISCOVERY_RESPONSE = Message.new('ECN', "TX-NR609/60128/DX/14DAE9E967C8\x19\r\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
13
+
14
+ # Create/start the server object.
15
+
16
+ def initialize
17
+ Socket.udp_server_loop(DISCOVERY_IP, Receiver::ONKYO_PORT) do |msg, src|
18
+ src.reply "ISCP\x00\x00\x00\x10\x00\x00\x00&\x01\x00\x00\x00!1ECNTX-NR609/60128/DX/14DAE9E967C8\x19\r\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
19
+ puts msg
20
+ end
21
+ end
22
+ end
23
+ end
data/bin/onkyo.rb CHANGED
@@ -3,6 +3,9 @@
3
3
  require 'eiscp'
4
4
  require 'optparse'
5
5
  require 'ostruct'
6
+
7
+ # This object parses ARGV and returns an @option hash
8
+ #
6
9
  class Options
7
10
  DEFAULT_OPTIONS = { verbose: true, all: false }
8
11
  USAGE = ' Usage: onkyo_rb [options]'
@@ -34,53 +37,77 @@ class Options
34
37
  @options.list_all = l
35
38
  end
36
39
 
37
- opts.on '-c', '--connect', 'Connect to the first discovered reciever and show updates' do |c|
38
- @options.connect = c
40
+ opts.on '-m', '--monitor', 'Connect to the first discovered reciever and monitor updates' do |m|
41
+ @options.monitor = m
39
42
  end
40
43
 
41
44
  end
42
45
 
43
46
  options.parse!(args)
44
47
 
45
- if @options == nil && ARGV == []
46
- puts options
47
- end
48
+ if @options.nil? && ARGV == [] then puts options end
48
49
 
49
50
  if @options.discover
50
- EISCP::Receiver.discover.each do |receiver|
51
- puts EISCP::Message.parse(receiver[0]).to_iscp
52
- end
51
+ EISCP::Receiver.discover.each {|rec| puts "#{rec.host}:#{rec.port} - #{rec.model} - #{rec.mac_address}"}
53
52
  exit 0
54
53
  end
55
54
 
56
55
  if @options.help
57
56
  puts options
58
- exit 0
57
+ exit 0
59
58
  end
60
59
 
61
- if @options.connect
62
- eiscp = EISCP::Receiver.new(EISCP::Receiver.discover[0][1])
63
- eiscp.connect do |data|
64
- puts msg = EISCP::Receiver.parse(data).to_iscp
60
+ if @options.monitor
61
+ begin
62
+ rec = EISCP::Receiver.new do |reply|
63
+ puts "Response: "\
64
+ "#{reply.zone.capitalize}: "\
65
+ "#{reply.command_description || reply.command} "\
66
+ "-> #{reply.value_description || reply.value}"
67
+ end
68
+ rec.thread.join
69
+ rescue Interrupt
70
+ fail 'Exiting...'
71
+ rescue Exception => e
72
+ puts "bummer..."
73
+ puts e
65
74
  end
66
75
  end
67
76
 
77
+ if @options.list_all
78
+ EISCP::Dictionary.zones.each do |zone|
79
+ EISCP::Dictionary.commands[zone].each do |command, command_hash|
80
+ puts "Command - Description"
81
+ puts "\n"
82
+ puts " '#{Dictionary.name_from_command(command)}' - "\
83
+ "#{Dictionary.description_from_command(command)}"
84
+ puts "\n"
85
+ puts " Value - Description>"
86
+ puts "\n"
87
+ command_hash[:values].each do |value, attr_hash|
88
+ puts " '#{attr_hash[:name]}' - "\
89
+ " #{attr_hash[:description]}"
90
+ end
91
+ puts "\n"
92
+ end
93
+ end
94
+ exit 0
95
+ end
96
+
68
97
  if ARGV == []
69
98
  puts options
70
99
  exit 0
71
100
  end
72
101
  end
73
-
74
102
  end
75
103
 
76
-
77
104
  @options = Options.parse(ARGV)
78
105
 
79
-
80
- receiver = EISCP::Receiver.new(EISCP::Receiver.discover[0][1])
81
- message = (EISCP::Message.parse(ARGV.join(" ")).to_eiscp)
82
- puts receiver.send_recv message
83
-
84
-
85
-
86
-
106
+ receiver = EISCP::Receiver.discover[0]
107
+ begin
108
+ command = EISCP::Parser.parse(ARGV.join(' '))
109
+ rescue
110
+ raise "Couldn't parse command"
111
+ end
112
+ reply = receiver.send_recv(command)
113
+ puts "Response from #{Receiver.host}: #{reply.zone.capitalize} #{reply.command_description || reply.command} -> #{reply.value_description || reply.value}"
@@ -2,6 +2,6 @@
2
2
 
3
3
  require 'eiscp/mock_receiver'
4
4
 
5
- puts "Starting server on 60128..."
5
+ puts 'Starting server on 60128...'
6
6
 
7
7
  EISCP::MockReceiver.new