Class: RUPNP::CP::RemoteService

Inherits:
Base
  • Object
show all
Defined in:
lib/rupnp/cp/remote_service.rb

Overview

Service class for device’s services.

Actions

This class defines ruby methods from actions defined in service description, as provided by the device.

By example, from this description:

<action>
  <name>actionName</name>
  <argumentList>
  <argument>
    <name>argumentNameIn</name>
    <direction>in</direction>
    <relatedStateVariable>stateVariableName</relatedStateVariable>
  </argument>
  <argument>
    <name>argumentNameOut</name>
    <direction>out</direction>
    <relatedStateVariable>stateVariableName</relatedStateVariable>
  </argument>
</action>

a #action_name method is created. This method requires a hash with an element named argument_name_in. If no in argument is required, an empty hash ({}) must be passed to the method. Hash keys may not be symbols.

A Hash is returned, with a key for each out argument.

Evented variables

Some variables in state (see #state_table, +:@send_events variable attribute) are evented. Events to update these variables are received only after subscription. To subscribe, use #subscribe_to_event.

After subscribing to events, state variables are automagically updated on events. Their value may be accessed through #variables.

A block may be passed to #subscribe_to_event to do a user action on receiving events.

service.subscribe_to_event do |msg|
  puts "receive #{msg}"
end

Author:

  • Sylvain Daubert

Constant Summary collapse

INTEGER_TYPES =
%w(ui1 ui2 ui4 i1 i2 i4 int).freeze
FLOAT_TYPES =
%w(r4 r8 number float).freeze
STRING_TYPES =
%w(char string uuid).freeze
TRUE_TYPES =
%w(1 true yes).freeze
FALSE_TYPES =
%w(0 false no).freeze
@@event_sub_count =
0

Constants inherited from Base

Base::HTTP_COMMON_CONFIG

Constants included from LogMixin

LogMixin::LOG_LEVEL

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#get_description, #inspect

Methods included from LogMixin

#log

Methods included from Tools

#build_url, #snake_case, #urn_are_equivalent?, #usn2udn

Constructor Details

#initialize(device, url_base, service) ⇒ RemoteService

Returns a new instance of RemoteService.

Parameters:

  • device (Device)
  • url_base (String)
  • service (Hash)


115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rupnp/cp/remote_service.rb', line 115

def initialize(device, url_base, service)
  super()
  @device = device
  @description = service

  @type = service[:service_type].to_s
  @id = service[:service_id].to_s
  @scpd_url = build_url(url_base,  service[:scpdurl].to_s)
  @control_url =  build_url(url_base, service[:control_url].to_s)
  @event_sub_url =  build_url(url_base, service[:event_sub_url].to_s)
  @actions = []
  @variables = {}
  @update_variables = EM::Channel.new

  @update_variables.subscribe do |var, value|
    @variables[var] = value
    log :debug, "varibale #{var} set to #{value}"
  end

  initialize_savon
end

Instance Attribute Details

#actionsArray<Hash> (readonly)

Available actions on this service

Returns:

  • (Array<Hash>)


104
105
106
# File 'lib/rupnp/cp/remote_service.rb', line 104

def actions
  @actions
end

#control_urlString (readonly)

URL for control

Returns:

  • (String)


91
92
93
# File 'lib/rupnp/cp/remote_service.rb', line 91

def control_url
  @control_url
end

#deviceDevice (readonly)

Get device to which this service belongs to

Returns:

  • (Device)


78
79
80
# File 'lib/rupnp/cp/remote_service.rb', line 78

def device
  @device
end

#event_sub_urlString (readonly)

URL for eventing

Returns:

  • (String)


94
95
96
# File 'lib/rupnp/cp/remote_service.rb', line 94

def event_sub_url
  @event_sub_url
end

#idString (readonly)

Get service id

Returns:

  • (String)


85
86
87
# File 'lib/rupnp/cp/remote_service.rb', line 85

def id
  @id
end

#scpd_urlString (readonly)

URL for service description

Returns:

  • (String)


88
89
90
# File 'lib/rupnp/cp/remote_service.rb', line 88

def scpd_url
  @scpd_url
end

#spec_versionString (readonly)

Define architecture on which the service is implemented

Returns:

  • (String)


101
102
103
# File 'lib/rupnp/cp/remote_service.rb', line 101

def spec_version
  @spec_version
end

#state_tableArray<Hash> (readonly)

State table for the service

Returns:

  • (Array<Hash>)


107
108
109
# File 'lib/rupnp/cp/remote_service.rb', line 107

def state_table
  @state_table
end

#typeString (readonly)

Get service type

Returns:

  • (String)


82
83
84
# File 'lib/rupnp/cp/remote_service.rb', line 82

def type
  @type
end

#variablesHash (readonly)

Variables from state table

Returns:

  • (Hash)


110
111
112
# File 'lib/rupnp/cp/remote_service.rb', line 110

def variables
  @variables
end

#xmlnsString (readonly)

XML namespace for device description

Returns:

  • (String)


98
99
100
# File 'lib/rupnp/cp/remote_service.rb', line 98

def xmlns
  @xmlns
end

Class Method Details

.event_sub_countInteger

Get event subscription count for all services (unique ID for subscription)

Returns:

  • (Integer)


60
61
62
# File 'lib/rupnp/cp/remote_service.rb', line 60

def self.event_sub_count
  @@event_sub_count += 1
end

Instance Method Details

#fetchvoid

This method returns an undefined value.

Get service from its description



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/rupnp/cp/remote_service.rb', line 139

def fetch
  scpd_getter = EM::DefaultDeferrable.new

  scpd_getter.errback do
    fail "cannot get SCPD from #@scpd_url"
    next
  end

  scpd_getter.callback do |scpd|
    if bad_description?(scpd)
      fail 'not a UPNP 1.0/1.1 SCPD'
      next
    end

    extract_service_state_table scpd
    extract_actions scpd

    succeed self
  end

  get_description @scpd_url, scpd_getter
end

#subscribe_to_event(options = {}) {|event, msg| ... } ⇒ Integer

Subscribe to event

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • timeout (Integer)

Yield Parameters:

  • event (Event)

    event received

  • msg (Object)

    message received

Returns:

  • (Integer)

    subscribe id. May be used to unsubscribe on event

Since:

  • 0.3.0



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/rupnp/cp/remote_service.rb', line 169

def subscribe_to_event(options={}, &blk)
  cp = device.control_point

  cp.start_event_server

  port = cp.event_port
  num = self.class.event_sub_count
  @callback_url = "http://#{HOST_IP}:#{port}/events/#{num}"

  uri = URI(@event_sub_url)
  options[:timeout] ||= EVENT_SUB_DEFAULT_TIMEOUT

  log :info, "send SUBSCRIBE request to #{uri}"
  con = EM::HttpRequest.new(@event_sub_url)
  http = con.setup_request(:subscribe, :head => {
                             'HOST' => "#{uri.host}:#{uri.port}",
                             'USER-AGENT' => RUPNP::USER_AGENT,
                             'CALLBACK' => @callback_url,
                             'NT' => 'upnp:event',
                             'TIMEOUT' => "Second-#{options[:timeout]}"})

  http.errback do |client|
    log :warn, "Cannot subscribe to event: #{client.error}"
  end

  http.callback do
    log :debug, 'Close connection to subscribe event URL'
    con.close
    if http.response_header.status != 200
      log :warn, "Cannot subscribe to event #@event_sub_url:" +
        " #{http.response_header.http_reason}"
    else
      timeout = http.response_header['TIMEOUT'].match(/(\d+)/)[1] || 1800
      event = Event.new(@event_sub_url, URI(@callback_url).path,
                        http.response_header['SID'], timeout.to_i)
      EventServer.add_event event
      log :info, 'event subscribtion registered'
      log :debug, "event: #{event.inspect}"

      event.subscribe do |msg|
        log :debug, "event #{event} received"
        if msg.is_a? Hash and msg[:content].is_a? Hash
          msg[:content].each do |k, v|
            if @variables.has_key? k
              log :info, "update evented variable #{k}"
              type = get_data_type_from_table(k)
              @update_variables << [k, get_value_from_type(type, v)]
            end
          end
        end
        log :debug, "call user block"
        blk.call(msg) if blk
      end
    end
  end
end