class StrDn2030::Remote
Constants
- INPUTLIST_REGEXP
- INPUT_REGEXP
- STATUS_REGEXP
- VOLUME_STATUS_REGEXP
Attributes
host[R]
inputs[R]
port[R]
Public Class Methods
new(host, port = 33335)
click to toggle source
# File lib/str_dn_2030.rb, line 20 def initialize(host, port = 33335) @host, @port = host, port @socket = nil @receiver_thread = nil @lock = Mutex.new @receiver_lock = Mutex.new @hook = nil @listeners = {} @listeners_lock = Mutex.new @inputs = {} @statuses = {} end
Public Instance Methods
active_input_get(zone_id)
click to toggle source
# File lib/str_dn_2030.rb, line 123 def active_input_get(zone_id) current = status_get(zone_id)[:ch][:video] inputs[zone_id][current] || self.reload_input.inputs[zone_id][current] end
active_input_set(zone_id, other)
click to toggle source
# File lib/str_dn_2030.rb, line 128 def active_input_set(zone_id, other) new_input = if other.is_a?(Input) raise ArgumentError, "#{other.inspect} is not in zone #{zone_id}" unless other.zone == zone_id other else inputs[zone_id][other] || self.reload_input.inputs[zone_id][other] end raise ArgumentError, "#{other.inspect} not exists" unless new_input zone = zone_id.chr('ASCII-8BIT') send "\x02\x04\xa0\x42".b + zone + new_input.video + "\x00".b listen(:success) other end
connect()
click to toggle source
# File lib/str_dn_2030.rb, line 70 def connect disconnect if connected? @lock.synchronize do @socket = TCPSocket.open(@host, @port) start_receiver reload_input self end end
connected?()
click to toggle source
# File lib/str_dn_2030.rb, line 66 def connected? !!@socket end
disconnect()
click to toggle source
# File lib/str_dn_2030.rb, line 81 def disconnect @lock.synchronize do return unless @socket @socket.close unless @socket.closed? @socket = nil stop_receiver end end
hook(&block)
click to toggle source
# File lib/str_dn_2030.rb, line 43 def hook(&block) if block_given? @hook = block else @hook end end
inspect()
click to toggle source
# File lib/str_dn_2030.rb, line 39 def inspect "#<#{self.class.name}: #{@host}:#{@port}>" end
listen(type, filter = nil)
click to toggle source
# File lib/str_dn_2030.rb, line 51 def listen(type, filter = nil) Thread.current[:strdn2030_filter] = filter @listeners_lock.synchronize { @listeners[type] ||= [] @listeners[type] << Thread.current } sleep data = Thread.current[:strdn2030_data] Thread.current[:strdn2030_data] = nil data end
mute_set(zone_id, new_mute)
click to toggle source
# File lib/str_dn_2030.rb, line 144 def mute_set(zone_id, new_mute) zone = zone_id.chr('ASCII-8BIT') send "\x02\x04\xa0\x53".b + zone + (new_mute ? "\x01".b : "\x00".b) + "\x00".b listen(:success) new_mute end
reload()
click to toggle source
# File lib/str_dn_2030.rb, line 90 def reload reload_input @statuses = {} self end
reload_input()
click to toggle source
# File lib/str_dn_2030.rb, line 151 def reload_input @inputs = {} get_input_list(0, 0) listen(:input_list, 0) get_input_list(0, 1) listen(:input_list, 0) get_input_list(1, 0) listen(:input_list, 1) get_input_list(1, 1) listen(:input_list, 1) self end
status_get(zone_id = 0)
click to toggle source
These are private api
# File lib/str_dn_2030.rb, line 102 def status_get(zone_id = 0) @statuses[zone_id] || begin zone = zone_id.chr('ASCII-8BIT') send "\x02\x03\xA0\x82".b + zone + "\x00".b listen(:status, zone_id) end end
volume_get(zone_id, type = "\x03".b)
click to toggle source
# File lib/str_dn_2030.rb, line 110 def volume_get(zone_id, type = "\x03".b) zone = zone_id.chr('ASCII-8BIT') send "\x02\x04\xa0\x92".b + zone + type + "\x00".b listen(:volume, zone_id) end
volume_set(zone_id, other, type = "\x03".b)
click to toggle source
# File lib/str_dn_2030.rb, line 116 def volume_set(zone_id, other, type = "\x03".b) zone = zone_id.chr('ASCII-8BIT') send "\x02\x06\xa0\x52".b + zone + type + [other.to_i].pack('s>') + "\x00".b listen(:success) other end
zone(zone_id)
click to toggle source
# File lib/str_dn_2030.rb, line 96 def zone(zone_id) Zone.new(self, zone_id.is_a?(String) ? zone_id.ord : zone_id.to_i) end
Private Instance Methods
debug(*args)
click to toggle source
# File lib/str_dn_2030.rb, line 287 def debug(*args) p(*args) if ENV["STR_DN_2030_DEBUG"] end
delegate(name, subtype = nil, *args)
click to toggle source
# File lib/str_dn_2030.rb, line 225 def delegate(name, subtype = nil, *args) wake_listener name, subtype, *args @hook.call(name, *args) if @hook end
get_input_list(zone_id, page = 0)
click to toggle source
# File lib/str_dn_2030.rb, line 188 def get_input_list(zone_id, page = 0) send("\x02\x04\xa0\x8b".b + zone_id.chr('ASCII-8BIT') + page.chr('ASCII-8BIT') + "\x00".b) end
handle_input_list(m)
click to toggle source
# File lib/str_dn_2030.rb, line 263 def handle_input_list(m) #p m inputs = {} zone = m['zone'].ord m['inputs'].scan(INPUT_REGEXP).each do |input_line| idx, audio, video, icon, preset_name, name, skip_flags = input_line input = inputs[audio] = inputs[video] = Input.new( self, zone, idx, audio, video, icon, preset_name, name, skip_flags, ) inputs[input.name] = input end (@inputs[zone] ||= {}).merge! inputs delegate :input_list, zone, inputs end
handle_status(m)
click to toggle source
# File lib/str_dn_2030.rb, line 242 def handle_status(m) flag1 = m['flag1'].ord flags = { raw: [m['flag1']].map{ |_| _.ord.to_s(2).rjust(8,' ') }, power: flag1[0] == 1, mute: flag1[1] == 1, headphone: flag1[2] == 1, unknown_6: flag1[5], } data = @statuses[m['zone'].ord] = { zone: m['zone'], ch: {audio: m['ch'], video: m['ch2']}, flags: flags } delegate(:status, m['zone'].ord, data) end
handle_volume_status(m)
click to toggle source
# File lib/str_dn_2030.rb, line 258 def handle_volume_status(m) data = {zone: m['zone'], type: m['type'], volume: m['volume'].unpack('s>').first} delegate(:volume, m['zone'].ord, data) end
receiver(socket)
click to toggle source
# File lib/str_dn_2030.rb, line 198 def receiver(socket) buffer = "".b hit = false handle = lambda do |pattern, &handler| if m = buffer.match(pattern) hit = true buffer.replace(m.pre_match + m.post_match) handler[m] end end while chunk = socket.read(1) hit = false buffer << chunk.b handle.(STATUS_REGEXP, &method(:handle_status)) handle.(VOLUME_STATUS_REGEXP, &method(:handle_volume_status)) handle.(INPUTLIST_REGEXP, &method(:handle_input_list)) handle.(/\A\xFD/n) { delegate(:success) } handle.(/\A\xFE/n) { delegate(:error) } debug([:buffer_remain, buffer]) if hit && !buffer.empty? end end
receiver_alive?()
click to toggle source
# File lib/str_dn_2030.rb, line 166 def receiver_alive? @receiver_thread && @receiver_thread.alive? end
send(str)
click to toggle source
# File lib/str_dn_2030.rb, line 192 def send(str) start_receiver(true) debug [:send, str] @socket.write str end
start_receiver(if_dead=false)
click to toggle source
# File lib/str_dn_2030.rb, line 170 def start_receiver(if_dead=false) return if if_dead && receiver_alive? stop_receiver @receiver_lock.synchronize do debug [:start_receiver] @receiver_thread = Thread.new(@socket, &method(:receiver)) @receiver_thread.abort_on_exception = true end end
stop_receiver()
click to toggle source
# File lib/str_dn_2030.rb, line 180 def stop_receiver @receiver_lock.synchronize do debug [:stop_receiver] @receiver_thread.kill if receiver_alive? @receiver_thread = nil end end
wake_listener(type, subtype, data = nil)
click to toggle source
# File lib/str_dn_2030.rb, line 230 def wake_listener(type, subtype, data = nil) @listeners_lock.synchronize do @listeners[type] ||= [] @listeners[type].each do |th| next if th[:strdn2030_filter] && !(th[:strdn2030_filter] === subtype) th[:strdn2030_data] = data th.wakeup end @listeners[type].clear end end