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.



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
# File 'lib/eiscp/receiver.rb', line 49

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 { |msg| 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]
    connect(&block) if block_given?
  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.
  #
  if host.nil?
    first_found = Receiver.discover[0]
    set_host.call first_found.host
    set_attrs.call first_found.ecn_hash
  elsif 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



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

def area
  @area
end

#hostObject

Receiver’s IP address



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

def host
  @host
end

#mac_addressObject

Receiver’s MAC address



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

def mac_address
  @mac_address
end

#modelObject

Receiver’s model string



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

def model
  @model
end

#portObject

Receiver’s ISCP port



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

def port
  @port
end

#socketObject (readonly)

Receiver’s connection socket



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

def socket
  @socket
end

#stateObject

State object



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

def state
  @state
end

#threadObject (readonly)

Receiver’s connection thread



37
38
39
# File 'lib/eiscp/receiver.rb', line 37

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.



118
119
120
121
122
123
# File 'lib/eiscp/receiver.rb', line 118

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

#disconnectObject

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



128
129
130
131
# File 'lib/eiscp/receiver.rb', line 128

def disconnect
  @thread.kill
  @socket.close
end

#ecn_hashObject

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



163
164
165
166
167
168
# File 'lib/eiscp/receiver.rb', line 163

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.



172
173
174
175
176
177
178
# File 'lib/eiscp/receiver.rb', line 172

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

#recvObject

Reads the socket and returns and EISCP::Message



145
146
147
148
149
150
# File 'lib/eiscp/receiver.rb', line 145

def recv
  data = String.new
  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



135
136
137
138
139
140
141
# File 'lib/eiscp/receiver.rb', line 135

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.



154
155
156
157
158
159
# File 'lib/eiscp/receiver.rb', line 154

def send_recv(eiscp)
  eiscp = Parser.parse(eiscp) if eiscp.is_a? String
  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.



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/eiscp/receiver.rb', line 183

def update_state
  Thread.new do
    Dictionary.commands.each do |zone, _commands|
      Dictionary.commands[zone].each do |command, info|
        info[:values].each do |value, _|
          next unless 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