Viewing file: tube_service.py (13.63 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# Licensed under the MIT license # http://opensource.org/licenses/mit-license.php
# Copyright 2009 - Frank Scholz <coherence@beebits.net>
""" TUBE service classes
"""
import urllib, urlparse
import dbus
from twisted.web import resource from twisted.internet import defer from twisted.python.util import OrderedDict
from coherence.upnp.core.utils import parse_xml
from coherence.upnp.devices.basics import RootDeviceXML, DeviceHttpRoot from coherence.upnp.core import service from coherence.upnp.core.soap_service import UPnPPublisher
from coherence.upnp.core import action from coherence.upnp.core import variable
from coherence.upnp.core import DIDLLite from coherence.upnp.core.utils import ReverseProxyUriResource
from coherence import log
class MirabeauProxy(resource.Resource, log.Loggable): logCategory = 'mirabeau'
def __init__(self): resource.Resource.__init__(self) log.Loggable.__init__(self) self.isLeaf = 0
def getChildWithDefault(self, path, request): self.info('MiraBeau getChildWithDefault %s, %s, %s %s' % (request.method, path, request.uri, request.client)) uri = urllib.unquote_plus(path) self.info('MiraBeau uri %r' % uri) return ReverseProxyUriResource(uri)
class TubeServiceControl(UPnPPublisher):
def __init__(self, server): self.service = server self.variables = server.get_variables() self.actions = server.get_actions()
def get_action_results(self, result, action, instance): """ check for out arguments if yes: check if there are related ones to StateVariables with non A_ARG_TYPE_ prefix if yes: check if there is a call plugin method for this action if yes: update StateVariable values with call result if no: get StateVariable values and add them to result dict """ self.debug('get_action_results', result) #print 'get_action_results', action, instance notify = [] for argument in action.get_out_arguments(): #print 'get_state_variable_contents', argument.name if argument.name[0:11] != 'A_ARG_TYPE_': variable = self.variables[instance][argument.get_state_variable()] variable.update(result[argument.name].decode('utf-8').encode('utf-8')) #print 'update state variable contents', variable.name, variable.value, variable.send_events if(variable.send_events == 'yes' and variable.moderated == False): notify.append(variable)
self.service.propagate_notification(notify) self.info( 'action_results unsorted', action.name, result) if len(result) == 0: return result ordered_result = OrderedDict() for argument in action.get_out_arguments(): if action.name == 'XXXBrowse' and argument.name == 'Result': didl = DIDLLite.DIDLElement.fromString(result['Result'].decode('utf-8')) changed = False for item in didl.getItems(): new_res = DIDLLite.Resources() for res in item.res: remote_protocol,remote_network,remote_content_format,_ = res.protocolInfo.split(':') if remote_protocol == 'http-get' and remote_network == '*': quoted_url = urllib.quote_plus(res.data) #print "modifying", res.data res.data = urlparse.urlunsplit(('http', self.service.device.external_address,'mirabeau',quoted_url,"")) #print "--->", res.data new_res.append(res) changed = True item.res = new_res if changed == True: didl.rebuild() ordered_result[argument.name] = didl.toString().replace('<ns0:','<') else: ordered_result[argument.name] = result[argument.name].decode('utf-8') else: ordered_result[argument.name] = result[argument.name].decode('utf-8').encode('utf-8') self.info( 'action_results sorted', action.name, ordered_result) return ordered_result
def soap__generic(self, *args, **kwargs): """ generic UPnP service control method, which will be used if no soap_ACTIONNAME method in the server service control class can be found """ try: action = self.actions[kwargs['soap_methodName']] except: return failure.Failure(errorCode(401))
try: instance = int(kwargs['InstanceID']) except: instance = 0
self.info("soap__generic", action, __name__, kwargs) del kwargs['soap_methodName']
in_arguments = action.get_in_arguments() for arg_name, arg in kwargs.iteritems(): if arg_name.find('X_') == 0: continue l = [ a for a in in_arguments if arg_name == a.get_name()] if len(l) > 0: in_arguments.remove(l[0]) else: self.critical('argument %s not valid for action %s' % (arg_name,action.name)) return failure.Failure(errorCode(402)) if len(in_arguments) > 0: self.critical('argument %s missing for action %s' % ([ a.get_name() for a in in_arguments],action.name)) return failure.Failure(errorCode(402))
def got_error(x): self.info('dbus error during call processing') return x
# call plugin method for this action #print 'callit args', args #print 'callit kwargs', kwargs #print 'callit action', action #print 'callit dbus action', self.service.service.action d = defer.Deferred() self.service.service.call_action( action.name, dbus.Dictionary(kwargs,signature='ss'), reply_handler = d.callback, error_handler = d.errback,utf8_strings=True) d.addCallback( self.get_action_results, action, instance) d.addErrback(got_error) return d
class TubeServiceProxy(service.ServiceServer, resource.Resource, log.Loggable): logCategory = 'dbus'
def __init__(self, tube_service, device, backend=None): self.device = device self.service = tube_service resource.Resource.__init__(self) id = self.service.get_id().split(':')[3] service.ServiceServer.__init__(self, id, self.device.version, None)
self.control = TubeServiceControl(self) self.putChild(self.scpd_url, service.scpdXML(self, self.control)) self.putChild(self.control_url, self.control) self.device.web_resource.putChild(id, self)
def init_var_and_actions(self): """ retrieve all actions and create the Action classes for our (proxy) server
retrieve all variables and create the StateVariable classes for our (proxy) server """ xml = self.service.get_scpd_xml() tree = parse_xml(xml, 'utf-8').getroot() ns = "urn:schemas-upnp-org:service-1-0"
for action_node in tree.findall('.//{%s}action' % ns): name = action_node.findtext('{%s}name' % ns) arguments = [] for argument in action_node.findall('.//{%s}argument' % ns): arg_name = argument.findtext('{%s}name' % ns) arg_direction = argument.findtext('{%s}direction' % ns) arg_state_var = argument.findtext('{%s}relatedStateVariable' % ns) arguments.append(action.Argument(arg_name, arg_direction, arg_state_var)) self._actions[name] = action.Action(self, name, 'n/a', arguments)
for var_node in tree.findall('.//{%s}stateVariable' % ns): send_events = var_node.attrib.get('sendEvents','yes') name = var_node.findtext('{%s}name' % ns) data_type = var_node.findtext('{%s}dataType' % ns) values = [] """ we need to ignore this, as there we don't get there our {urn:schemas-beebits-net:service-1-0}X_withVendorDefines attibute there """ for allowed in var_node.findall('.//{%s}allowedValue' % ns): values.append(allowed.text) instance = 0 self._variables.get(instance)[name] = variable.StateVariable(self, name, 'n/a', instance, send_events, data_type, values) """ we need to do this here, as there we don't get there our {urn:schemas-beebits-net:service-1-0}X_withVendorDefines attibute there """ self._variables.get(instance)[name].has_vendor_values = True
class TubeDeviceProxy(log.Loggable): logCategory = 'dbus'
def __init__(self, coherence, tube_device,external_address): log.Loggable.__init__(self) self.device = tube_device self.coherence = coherence self.external_address = external_address self.uuid = self.device.get_id().split('-') self.uuid[1] = 'tube' self.uuid = '-'.join(self.uuid) self.friendly_name = self.device.get_friendly_name() self.device_type = self.device.get_friendly_device_type() self.version = int(self.device.get_device_type_version())
self._services = [] self._devices = [] self.icons = []
self.info("uuid: %s, name: %r, device type: %r, version: %r", self.uuid, self.friendly_name, self.device_type, self.version)
""" create the http entrypoint """
self.web_resource = DeviceHttpRoot(self) self.coherence.add_web_resource( str(self.uuid)[5:], self.web_resource)
""" create the Service proxy(s) """
for service in self.device.services: self.debug("Proxying service %r", service) new_service = TubeServiceProxy(service, self) self._services.append(new_service)
""" create a device description xml file(s) """
version = self.version while version > 0: self.web_resource.putChild( 'description-%d.xml' % version, RootDeviceXML( self.coherence.hostname, str(self.uuid), self.coherence.urlbase, device_type=self.device_type, version=version, friendly_name=self.friendly_name, model_description='Coherence UPnP %s' % self.device_type, model_name='Coherence UPnP %s' % self.device_type, services=self._services, devices=self._devices, icons=self.icons)) version -= 1
""" and register with SSDP server """ self.register()
def register(self): s = self.coherence.ssdp_server uuid = str(self.uuid) host = self.coherence.hostname self.msg('%s register' % self.device_type) # we need to do this after the children are there, since we send notifies s.register('local', '%s::upnp:rootdevice' % uuid, 'upnp:rootdevice', self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % self.version, host=host)
s.register('local', uuid, uuid, self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % self.version, host=host)
version = self.version while version > 0: if version == self.version: silent = False else: silent = True s.register('local', '%s::urn:schemas-upnp-org:device:%s:%d' % (uuid, self.device_type, version), 'urn:schemas-upnp-org:device:%s:%d' % (self.device_type, version), self.coherence.urlbase + uuid[5:] + '/' + 'description-%d.xml' % version, silent=silent, host=host) version -= 1
for service in self._services: device_version = self.version service_version = self.version if hasattr(service,'version'): service_version = service.version silent = False
while service_version > 0: try: namespace = service.namespace except: namespace = 'schemas-upnp-org'
device_description_tmpl = 'description-%d.xml' % device_version if hasattr(service,'device_description_tmpl'): device_description_tmpl = service.device_description_tmpl
s.register('local', '%s::urn:%s:service:%s:%d' % (uuid,namespace,service.id, service_version), 'urn:%s:service:%s:%d' % (namespace,service.id, service_version), self.coherence.urlbase + uuid[5:] + '/' + device_description_tmpl, silent=silent, host=host)
silent = True service_version -= 1 device_version -= 1
|