Class: UPnP::Service
- Inherits:
-
SOAP::RPC::StandaloneServer
- Object
- SOAP::RPC::StandaloneServer
- UPnP::Service
- Defined in:
- lib/UPnP/service.rb
Overview
A service contains a SOAP endpoint and the Service Control Protocol Definition (SCPD). It acts as a SOAP server that is mounted onto the RootServer along with the containing devices and other devices and services in a UPnP device.
Creating a UPnP::Service class
A concrete UPnP service looks like this:
require 'UPnP/service'
class UPnP::Service::ContentDirectory < UPnP::Service
add_action 'Browse',
[IN, 'ObjectID', 'A_ARG_TYPE_ObjectID'],
# ...
[OUT, 'Result', 'A_ARG_TYPE_Result'],
# ...
add_variable 'A_ARG_TYPE_ObjectID', 'string'
add_variable 'A_ARG_TYPE_Result', 'string'
def Browse(object_id, ...)
# ...
[nil, result]
end
end
Subclass UPnP::Service in the UPnP::Service namespace. UPnP::Service looks in its own namespace for various information when instantiating the service.
Service Control Protocol Definition
#add_action defines a service’s action. The action’s arguments follow the name as arrays of direction (IN, OUT, RETVAL), argument name, and related state variable.
#add_variable defines a state table variable. The name is followed by the type, allowed values, default value and whether or not the variable is evented.
Implementing methods
Define a regular ruby method matching the name in add_action for soap4r to call when it receives a request. It will be called with the IN parameters in order. The method needs to return an Array of OUT parameters in-order. If there is no RETVAL, the first item in the Array should be nil.
Instantiating a UPnP::Service
A UPnP::Service will be instantiated automatically for you if you call add_service in the UPnP::Device initialization block. If you want to instantiate a service by hand, use ::create to pick the correct subclass automatically.
Direct Known Subclasses
Defined Under Namespace
Classes: Error, Filter, TestService
Constant Summary collapse
- ACTIONS =
Maps actions for a service to their arguments
Hash.new { |h, service| h[service] = {} }
- VARIABLES =
Maps state variables for a service to their variable information
Hash.new { |h, service| h[service] = {} }
- IN =
SOAP input argument type
SOAP::RPC::SOAPMethod::IN
- OUT =
SOAP output argument type
SOAP::RPC::SOAPMethod::OUT
- RETVAL =
SOAP return value argument type
SOAP::RPC::SOAPMethod::RETVAL
- SCHEMA_URN =
UPnP 1.0 service schema
'urn:schemas-upnp-org:service-1-0'
Instance Attribute Summary collapse
-
#device ⇒ Object
readonly
This service’s parent.
-
#type ⇒ Object
readonly
Type of UPnP service.
Class Method Summary collapse
-
.add_action(name, *arguments) ⇒ Object
Adds the action
name
to this class witharguments
. -
.add_variable(name, type, allowed_values = nil, default = nil, evented = false) ⇒ Object
Adds a state variable
name
to this class. -
.create(device, type, &block) ⇒ Object
Creates a new service under
device
of the giventype
.
Instance Method Summary collapse
-
#actions ⇒ Object
Actions for this service.
-
#add_actions ⇒ Object
Adds RPC actions to this service.
-
#cache_dir ⇒ Object
A directory for storing service-specific persistent data.
-
#control_url ⇒ Object
The control URL for this service.
-
#create_config ⇒ Object
Tell the StandaloneServer to not listen, RootServer does this for us.
-
#description(xml) ⇒ Object
Adds a description of this service to XML::Builder
xml
. -
#device_path ⇒ Object
The path for this service’s parent device.
-
#event_sub_url ⇒ Object
The event subscription url for this service.
-
#initialize(device, type) {|_self| ... } ⇒ Service
constructor
Creates a new service under
device
of the giventype
. -
#marshal_dump ⇒ Object
Dumps only information necessary to run initialize.
-
#marshal_load(data) ⇒ Object
Loads data and initializes the server.
-
#mount_extra(http_server) ⇒ Object
Callback to mount extra WEBrick servlets.
-
#root_device ⇒ Object
The root device for this service.
-
#scpd ⇒ Object
The SCPD for this service.
-
#scpd_action_list(xml) ⇒ Object
Adds the SCPD actionList to XML::Builder
xml
. -
#scpd_service_state_table(xml) ⇒ Object
Adds the SCPD serviceStateTable to XML::Builder
xml
. -
#scpd_url ⇒ Object
The SCPD url for this service.
-
#service_path ⇒ Object
The HTTP path to this service.
-
#type_urn ⇒ Object
URN of this service’s type.
-
#variables ⇒ Object
Returns a Hash of state variables for this service.
Constructor Details
#initialize(device, type) {|_self| ... } ⇒ Service
Creates a new service under device
of the given type
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/UPnP/service.rb', line 155 def initialize(device, type, &block) @device = device @type = type @cache_dir = nil # HACK PS3 disobeys spec SOAP::NS::KNOWN_TAG[type_urn] = 'u' SOAP::NS::KNOWN_TAG[SOAP::EnvelopeNamespace] = 's' super @type, type_urn filterchain.add Filter.new add_actions yield self if block_given? end |
Instance Attribute Details
#device ⇒ Object (readonly)
This service’s parent
118 119 120 |
# File 'lib/UPnP/service.rb', line 118 def device @device end |
#type ⇒ Object (readonly)
Type of UPnP service. Use type_urn for the full URN
123 124 125 |
# File 'lib/UPnP/service.rb', line 123 def type @type end |
Class Method Details
.add_action(name, *arguments) ⇒ Object
Adds the action name
to this class with arguments
128 129 130 |
# File 'lib/UPnP/service.rb', line 128 def self.add_action(name, *arguments) ACTIONS[self][name] = arguments end |
.add_variable(name, type, allowed_values = nil, default = nil, evented = false) ⇒ Object
Adds a state variable name
to this class
135 136 137 138 |
# File 'lib/UPnP/service.rb', line 135 def self.add_variable(name, type, allowed_values = nil, default = nil, evented = false) VARIABLES[self][name] = [type, allowed_values, default, evented] end |
.create(device, type, &block) ⇒ Object
Creates a new service under device
of the given type
. Requires a concrete subclass of UPnP::Service.
144 145 146 147 148 149 150 |
# File 'lib/UPnP/service.rb', line 144 def self.create(device, type, &block) klass = const_get type klass.new(device, type, &block) rescue NameError => e raise unless e. =~ /UPnP::Service::#{type}/ raise Error, "unknown service type #{type}" end |
Instance Method Details
#actions ⇒ Object
Actions for this service
177 178 179 |
# File 'lib/UPnP/service.rb', line 177 def actions ACTIONS[self.class] end |
#add_actions ⇒ Object
Adds RPC actions to this service
184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/UPnP/service.rb', line 184 def add_actions opts = { :request_style => :rpc, :response_style => :rpc, :request_use => :encoded, :response_use => :literal, } actions.each do |name, params| qname = XSD::QName.new @default_namespace, name param_def = SOAP::RPC::SOAPMethod.derive_rpc_param_def self, name, params @router.add_method self, qname, nil, name, param_def, opts end end |
#cache_dir ⇒ Object
A directory for storing service-specific persistent data
202 203 204 205 206 207 208 209 210 211 |
# File 'lib/UPnP/service.rb', line 202 def cache_dir return @cache_dir if @cache_dir @cache_dir = File.join '~', '.UPnP', '_cache', "#{@device.name}-#{@type}" @cache_dir = File. @cache_dir FileUtils.mkdir_p @cache_dir @cache_dir end |
#control_url ⇒ Object
The control URL for this service
216 217 218 |
# File 'lib/UPnP/service.rb', line 216 def control_url File.join service_path, 'control' end |
#create_config ⇒ Object
Tell the StandaloneServer to not listen, RootServer does this for us
223 224 225 226 227 |
# File 'lib/UPnP/service.rb', line 223 def create_config hash = super hash[:DoNotListen] = true hash end |
#description(xml) ⇒ Object
Adds a description of this service to XML::Builder xml
232 233 234 235 236 237 238 239 240 |
# File 'lib/UPnP/service.rb', line 232 def description(xml) xml.service do xml.serviceType type_urn xml.serviceId root_device.service_id(self) xml.SCPDURL scpd_url xml.controlURL control_url xml.eventSubURL event_sub_url end end |
#device_path ⇒ Object
The path for this service’s parent device
245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/UPnP/service.rb', line 245 def device_path devices = [] device = @device until device.nil? do devices << device device = device.parent end File.join('/', *devices.map { |d| d.type }) end |
#event_sub_url ⇒ Object
The event subscription url for this service
260 261 262 |
# File 'lib/UPnP/service.rb', line 260 def event_sub_url File.join service_path, 'event_sub' end |
#marshal_dump ⇒ Object
Dumps only information necessary to run initialize. Server state is not persisted.
268 269 270 271 272 273 |
# File 'lib/UPnP/service.rb', line 268 def marshal_dump [ @device, @type ] end |
#marshal_load(data) ⇒ Object
Loads data and initializes the server
278 279 280 281 282 283 284 285 |
# File 'lib/UPnP/service.rb', line 278 def marshal_load(data) device = data.shift type = data.shift initialize device, type add_actions end |
#mount_extra(http_server) ⇒ Object
Callback to mount extra WEBrick servlets
290 291 |
# File 'lib/UPnP/service.rb', line 290 def mount_extra(http_server) end |
#root_device ⇒ Object
The root device for this service
296 297 298 |
# File 'lib/UPnP/service.rb', line 296 def root_device @device.root_device end |
#scpd ⇒ Object
The SCPD for this service
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/UPnP/service.rb', line 303 def scpd xml = Builder::XmlMarkup.new :indent => 2 xml.instruct! xml.scpd :xmlns => SCHEMA_URN do xml.specVersion do xml.major 1 xml.minor 0 end scpd_action_list xml scpd_service_state_table xml end end |
#scpd_action_list(xml) ⇒ Object
Adds the SCPD actionList to XML::Builder xml
.
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/UPnP/service.rb', line 322 def scpd_action_list(xml) xml.actionList do actions.sort_by { |name,| name }.each do |name, arguments| xml.action do xml.name name xml.argumentList do arguments.each do |direction, arg_name, state_variable| xml.argument do xml.direction direction xml.name arg_name xml. state_variable end end end end end end end |
#scpd_service_state_table(xml) ⇒ Object
Adds the SCPD serviceStateTable to XML::Builder xml
.
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/UPnP/service.rb', line 344 def scpd_service_state_table(xml) xml.serviceStateTable do variables.each do |name, (type, allowed_values, default, send_events)| send_events = send_events ? 'yes' : 'no' xml.stateVariable :sendEvents => send_events do xml.name name xml.dataType type if allowed_values then xml.allowedValueList do allowed_values.each do |value| xml.allowedValue value end end end end end end end |
#scpd_url ⇒ Object
The SCPD url for this service
366 367 368 |
# File 'lib/UPnP/service.rb', line 366 def scpd_url service_path end |
#service_path ⇒ Object
The HTTP path to this service
373 374 375 |
# File 'lib/UPnP/service.rb', line 373 def service_path File.join device_path, @type end |
#type_urn ⇒ Object
URN of this service’s type
380 381 382 |
# File 'lib/UPnP/service.rb', line 380 def type_urn "#{UPnP::SERVICE_SCHEMA_PREFIX}:#{@type}:1" end |
#variables ⇒ Object
Returns a Hash of state variables for this service
387 388 389 |
# File 'lib/UPnP/service.rb', line 387 def variables VARIABLES[self.class] end |