Class: EISCP::Receiver

Inherits:
Object
  • Object
show all
Extended by:
Discovery
Includes:
CommandMethods
Defined in:
lib/eiscp/receiver.rb,
lib/eiscp/receiver/discovery.rb,
lib/eiscp/receiver/command_methods.rb

Overview

The EISCP::Receiver class is used to communicate with one or more receivers the network. A Receiver can be instantiated automatically using discovery, or by hostname and port.

receiver = EISCP::Receiver.new # find first receiver on LAN
receiver = EISCP::Receiver.new('192.168.1.12') # default port
receiver = EISCP::Receiver.new('192.168.1.12', 60129) # non standard port

Defined Under Namespace

Modules: CommandMethods, Discovery

Constant Summary collapse

DEFAULT_TIMEOUT =

Default connection timeout value in seconds

0.5
ONKYO_PORT =

Default Onkyo eISCP port

60_128

Constants included from Discovery

Discovery::ONKYO_MAGIC

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Discovery

discover, ecn_string_to_ecn_array

Methods included from CommandMethods

generate

Constructor Details

#initialize(host = nil, info_hash = {}, &block) ⇒ Receiver

Create a new EISCP::Receiver object to communicate with a receiver. If no host is given, use auto discovery and create a receiver object using the first host to respond.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/eiscp/receiver.rb', line 48

def initialize(host = nil, info_hash = {}, &block)
  # Initialize state
  #
  @state = {}
  # This defines the behavior of CommandMethods by telling it what to do
  # with the Message object that results from a CommandMethod being called.
  # All we're doing here is calling #send_recv
  #
  command_method_proc = Proc.new {|msg| self.send_recv msg}
  CommandMethods.generate(&command_method_proc)

  # This proc sets the four ECN attributes and initiates a connection to the
  # receiver.
  #
  set_attrs = lambda do |hash|
    @model = hash[:model]
    @port  = hash[:port]
    @area  = hash[:area]
    @mac_address = hash[:mac_address]
    if block_given?
      connect(&block)
    end
  end

  # This lambda sets the host IP after resolving it
  #
  set_host = lambda do |hostname|
    @host = Resolv.getaddress hostname
  end

  # When no host is given, the first discovered host is returned.
  #
  # When a host is given without a hash ::discover will be used to find
  # a receiver that matches.
  #
  # Else, use the given host and hash to create a new Receiver object.
  # This is how ::discover creates Receivers.
  #
  case
  when host.nil?
    first_found = Receiver.discover[0]
    set_host.call first_found.host
    set_attrs.call first_found.ecn_hash
  when info_hash.empty?
    set_host.call host
    Receiver.discover.each do |receiver|
      receiver.host == @host && set_attrs.call(receiver.ecn_hash)
    end
  else
    set_host.call host
    set_attrs.call info_hash
  end
end

Instance Attribute Details

#areaObject

Receiver’s region



25
26
27
# File 'lib/eiscp/receiver.rb', line 25

def area
  @area
end

#hostObject

Receiver’s IP address



19
20
21
# File 'lib/eiscp/receiver.rb', line 19

def host
  @host
end

#mac_addressObject

Receiver’s MAC address



27
28
29
# File 'lib/eiscp/receiver.rb', line 27

def mac_address
  @mac_address
end

#modelObject

Receiver’s model string



21
22
23
# File 'lib/eiscp/receiver.rb', line 21

def model
  @model
end

#portObject

Receiver’s ISCP port



23
24
25
# File 'lib/eiscp/receiver.rb', line 23

def port
  @port
end

#socketObject (readonly)

Receiver’s connection socket



33
34
35
# File 'lib/eiscp/receiver.rb', line 33

def socket
  @socket
end

#stateObject

State object



30
31
32
# File 'lib/eiscp/receiver.rb', line 30

def state
  @state
end

#threadObject (readonly)

Receiver’s connection thread



35
36
37
# File 'lib/eiscp/receiver.rb', line 35

def thread
  @thread
end

Instance Method Details

#connect(&block) ⇒ Object

This creates a socket conection to the receiver if one doesn’t exist, and updates or sets the callback block if one is passed.



120
121
122
123
124
125
126
127
# File 'lib/eiscp/receiver.rb', line 120

def connect(&block)
  begin
    @socket ||= TCPSocket.new(@host, @port)
    update_thread(&block)
  rescue => e
    puts e
  end
end

#disconnectObject

Disconnect from the receiver by closing the socket and killing the connection thread.



132
133
134
135
# File 'lib/eiscp/receiver.rb', line 132

def disconnect
  @thread.kill
  @socket.close
end

#ecn_hashObject

Return ECN hash with model, port, area, and MAC address



169
170
171
172
173
174
175
# File 'lib/eiscp/receiver.rb', line 169

def ecn_hash
  { model: @model,
    port: @port,
    area: @area,
    mac_address: @mac_address
  }
end

#human_readable_stateObject

This will return a human-readable represantion of the receiver’s state.



179
180
181
182
183
184
185
# File 'lib/eiscp/receiver.rb', line 179

def human_readable_state
  hash = {}
  @state.each do |c, v|
    hash["#{Dictionary.command_to_name(c)}"] =  "#{Dictionary.command_value_to_value_name(c, v) || v.to_s}"
  end
  hash
end

#recvObject

Reads the socket and returns and EISCP::Message



149
150
151
152
153
154
# File 'lib/eiscp/receiver.rb', line 149

def recv
  data = ''
  data << @socket.gets until data.match(/\r\n$/)
  message = Parser.parse(data)
  message
end

#send(eiscp) ⇒ Object

Sends an EISCP::Message object or string on the network



139
140
141
142
143
144
145
# File 'lib/eiscp/receiver.rb', line 139

def send(eiscp)
  if eiscp.is_a? EISCP::Message
    @socket.puts(eiscp.to_eiscp)
  elsif eiscp.is_a? String
    @socket.puts eiscp
  end
end

#send_recv(eiscp) ⇒ Object

Sends an EISCP::Message object or string on the network and returns recieved data string.



158
159
160
161
162
163
164
165
# File 'lib/eiscp/receiver.rb', line 158

def send_recv(eiscp)
  if eiscp.is_a? String
    eiscp = Parser.parse(eiscp)
  end
  send eiscp
  sleep DEFAULT_TIMEOUT
  Parser.parse("#{eiscp.command}#{@state[eiscp.command]}")
end

#update_stateObject

Runs every command that supports the ‘QSTN’ value. This is a good way to get the sate of the receiver after connecting.



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/eiscp/receiver.rb', line 190

def update_state
  Thread.new do
    Dictionary.commands.each do |zone, commands|
      Dictionary.commands[zone].each do |command, info|
        info[:values].each do |value, _|
          if value == 'QSTN'
            send(Parser.parse(command + "QSTN"))
            # If we send any faster we risk making the stereo drop replies. 
            # A dropped reply is not necessarily indicative of the
            # receiver's failure to receive the command and change state
            # accordingly. In this case, we're only making queries, so we do
            # want to capture every reply.
            sleep DEFAULT_TIMEOUT
          end
        end
      end
    end
  end
end