class UPnPControlPoint

Attributes

device_listener[RW]
event_listener[RW]
subscriptions[RW]

Public Class Methods

new(host = '0.0.0.0', port = 0) click to toggle source
# File lib/upnp_control_point.rb, line 49
def initialize(host = '0.0.0.0', port = 0)
  @finishing = false
  @ssdp_listener = SSDP::SsdpListener.new
  @ssdp_listener.handler = self
  @http_server = WEBrick::HTTPServer.new :BindAddress => host, :Port => port
  @http_server.mount '/', EventNotifyServlet, self
  @devices = {}
  @subscriptions = {}
  @interval_timer = 10
end

Public Instance Methods

build_device(ssdp_header) click to toggle source
# File lib/upnp_control_point.rb, line 106
def build_device(ssdp_header)
  uri = URI(ssdp_header.location)
  xml = Net::HTTP.get(uri)
end
get_ip_address() click to toggle source
# File lib/upnp_control_point.rb, line 112
def get_ip_address
  Socket::getaddrinfo(Socket.gethostname, 'echo', Socket::AF_INET)[0][3]
end
invoke_action(device, service, action_name, params) click to toggle source
# File lib/upnp_control_point.rb, line 152
def invoke_action(device, service, action_name, params)
  url = URI::join(device.base_url, service['controlURL'])
  soap_req = UPnPSoapRequest.new service.service_type, action_name
  soap_req.merge! params
  header = {
    'SOAPACTION' => "#{service.service_type}##{action_name}",
    'Content-Type' => 'text/xml; charset="utf-8'
  }
  http = Net::HTTP.new(url.host, url.port)
  req = Net::HTTP::Post.new(url.request_uri, header)
  req.body = soap_req.to_xml
  res = http.request(req)
  UPnPSoapResponse.read res.body
end
on_event_notify(sid, body) click to toggle source
# File lib/upnp_control_point.rb, line 63
def on_event_notify(sid, body)
  props = UPnPEventProperty.read(body)
  if @event_listener
    @event_listener.on_event_notify sid, props
  end
end
on_ssdp_header(ssdp_header) click to toggle source
# File lib/upnp_control_point.rb, line 71
def on_ssdp_header(ssdp_header)
  if ssdp_header.notify_alive? or ssdp_header.http_response?
    usn = Usn.read(ssdp_header.usn)
    if not @devices.key? usn.udn
      xml = self.build_device ssdp_header
      if xml.to_s.empty?
        return
      end
      device = UPnPDevice.read xml
      device.base_url = ssdp_header.location
      @devices[usn.udn] = device
      if @device_listener
        @device_listener.on_device_added device
      end
    end
  elsif ssdp_header.notify_byebye?
    usn = Usn.read(ssdp_header['usn'])
    device = @devices[usn.udn]
    if device
      if @device_listener
        @device_listener.on_device_removed device
      end
      @devices.delete usn.udn
    end
  end
end
on_timer() click to toggle source
# File lib/upnp_control_point.rb, line 168
def on_timer
  @devices.reject! {|key, value| value.expired? }
end
send_msearch(st, mx = 3) click to toggle source
# File lib/upnp_control_point.rb, line 99
def send_msearch(st, mx = 3)
  $logger.debug("send msearch / #{st}")
  SSDP.send_msearch st, mx, lambda {
    |ssdp_header| on_ssdp_header ssdp_header}
end
start() click to toggle source
# File lib/upnp_control_point.rb, line 183
def start
  @finishing = false
  @ssdp_listener_thread = Thread.new { @ssdp_listener.run }
  @http_server_thread = Thread.new { @http_server.start }
  @timer_thread = Thread.new { timer_loop }
end
stop() click to toggle source
# File lib/upnp_control_point.rb, line 191
def stop
  @finishing = true
  @http_server.shutdown
  @http_server_thread.join
  @timer_thread.join
end
subscribe(device, service) click to toggle source
# File lib/upnp_control_point.rb, line 117
def subscribe(device, service)
  host = self.get_ip_address
  port = @http_server.config[:Port]
  headers = {
    'NT' => 'upnp:event',
    'CALLBACK' => "<http://#{host}:#{port}/event>",
    'TIMEOUT' => 'Second-1800'
  }
  url = URI::join(device.base_url, service['eventSubURL'])
  Net::HTTP.start(url.host, url.port) { |http|
    req = SubscribeRequest.new url, initheader = headers
    res = http.request req
    sid = res['sid']
    timeout = res['timeout'].split('-')[-1]
    subscription = UPnPEventSubscription.new device, service, sid, timeout
    @subscriptions[sid] = subscription
    return subscription
  }
end
timer_loop() click to toggle source
# File lib/upnp_control_point.rb, line 172
def timer_loop
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
  while not @finishing
    dur = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) - start
    if dur >= @interval_timer
      on_timer
      start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
    end
  end
end
unsubscribe(subscription) click to toggle source
# File lib/upnp_control_point.rb, line 138
def unsubscribe(subscription)
  headers = {
    'SID' => subscription.sid,
  }
  url = URI::join(subscription.device.base_url, subscription.service.scpdurl)
  Net::HTTP.start(url.host, url.port) { |http|
    req = UnsubscribeRequest.new url, initheader = headers
    res = http.request req
    $logger.debug("response : #{res.code}'")
  }
  @subscriptions.delete(subscription.sid)
end