Class: UPnPControlPoint

Inherits:
Object
  • Object
show all
Defined in:
lib/upnp_control_point.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host = '0.0.0.0', port = 0) ⇒ UPnPControlPoint

Returns a new instance of UPnPControlPoint.



49
50
51
52
53
54
55
56
57
58
# 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

Instance Attribute Details

#device_listenerObject

Returns the value of attribute device_listener.



60
61
62
# File 'lib/upnp_control_point.rb', line 60

def device_listener
  @device_listener
end

#event_listenerObject

Returns the value of attribute event_listener.



60
61
62
# File 'lib/upnp_control_point.rb', line 60

def event_listener
  @event_listener
end

#subscriptionsObject

Returns the value of attribute subscriptions.



60
61
62
# File 'lib/upnp_control_point.rb', line 60

def subscriptions
  @subscriptions
end

Instance Method Details

#build_device(ssdp_header) ⇒ Object



106
107
108
109
# 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_addressObject



112
113
114
# 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) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
# 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) ⇒ Object



63
64
65
66
67
68
# 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) ⇒ Object



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
# 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_timerObject



168
169
170
# File 'lib/upnp_control_point.rb', line 168

def on_timer
  @devices.reject! {|key, value| value.expired? }
end

#send_msearch(st, mx = 3) ⇒ Object



99
100
101
102
103
# 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

#startObject



183
184
185
186
187
188
# 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

#stopObject



191
192
193
194
195
196
# 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) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# 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_loopObject



172
173
174
175
176
177
178
179
180
181
# 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) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
# 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