def __init__(self, config, webserver, deviceList): self.info("Create MyControlPoint") ControlPoint.__init__( self, webserver, deviceList ) # super(ControlPoint,self).__init__( ws, deviceList ) self._connections=[] # track them self._excludeList = None self._includeList = None # for now just on model name if config.has_key('excludedevice') and config['excludedevice'].has_key('modelName'): self._excludeList = list() excs = config['excludedevice']['modelName'] if isinstance( excs, list ): for ntry in excs: self.debug("excludedevice %s", ntry) self._excludeList.append( ntry[''].lower() ) else: self.debug("exclude %s", excs) self._excludeList.append( excs[''].lower() ) if config.has_key('includedevice') and config['includedevice'].has_key('modelName'): self._includeList = list() incs = config['includedevice']['modelName'] if isinstance( incs, list ): for ntry in incs: self.debug("include %s", ntry) self._includeList.append( ntry[''].lower() ) else: self.debug("includedevice %s", incs) self._includeList.append( incs[''].lower() )
def start(self): print "Upnp backend starting" # clear device list self._removeXML() # config Coherence conf = Config(self._configfile) c = Coherence({'logmode':'warning'}) c.setup(conf) #add share folders con = "" for f in config.share_folders: if len(con) > 0: con +="," con += f c.add_plugin('FSStore',name=config.device_name,content=con) # start a ControlPoint to start detecting upnp activity cp = ControlPoint(c, auto_client=['MediaServer']) cp.connect(self.mediaserver_detected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') cp.connect(self.mediaserver_removed, 'Coherence.UPnP.ControlPoint.MediaServer.removed') #self._localdevice = cp.get_device_by_name(self._localdevicename) #print "SPRURPDDSOPF ",self._localdevice.client.get_friendly_name() print "Upnp loop succesfully started" self._handler.start() print "DBus loop succesfully started"
class UPNP_Browser(): def __init__(self): self.browse_count = 0 self.music = Music_DB() self.__client = None self.control_point = ControlPoint(Coherence({'logmode':'warning'}), auto_client=['MediaServer']) self.control_point.connect(self.media_server_found, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self.control_point.connect(self.media_server_removed, 'Coherence.UPnP.ControlPoint.MediaServer.removed') # now we should also try to discover the ones that are already there: for device in self.control_point.coherence.devices: print device # called for each media server found def media_server_found(self, client, udn): print "media_server_found", client print "media_server_found", client.device.get_friendly_name() self.__client = client self.load_children(0) # sadly they sometimes get removed as well :( def media_server_removed(self, udn): print "media_server_removed", udn # browse callback def process_media_server_browse(self, result, client): print "browsing root of", client.device.get_friendly_name() print "result contains %d out of %d total matches" % \ (int(result['NumberReturned']), int(result['TotalMatches'])) elt = DIDLLite.DIDLElement.fromString(result['Result']) for item in elt.getItems(): if item.upnp_class.startswith("object.container"): self.load_children(item.id) if item.upnp_class.startswith("object.item"): url = None duration = None size = None for res in item.res: remote_protocol,remote_network,remote_content_format,remote_flags = res.protocolInfo.split(':') if remote_protocol == 'http-get': url = res.data duration = res.duration size = res.size break self.music.add_song(item, url, duration) def load_children(self, id): self.browse_count += 1 d = self.__client.content_directory.browse(id, browse_flag='BrowseDirectChildren', process_result=False, backward_compatibility=False) d.addCallback(self.process_media_server_browse, self.__client)
def check_device( self, device): self.info("found device %s of type %s",device.get_friendly_name(), device.get_device_type()) short_type = device.get_device_type().split(':')[3] if short_type == "ZonePlayer": self.info("identified ZonePlayer %s", device.get_friendly_name()) client = SonosZonePlayerClient(device) device.set_client(client) # event sent in self.completed louie.send('Coherence.UPnP.ControlPoint.ZonePlayer.detected', None, client=client,udn=device.get_id()) ControlPoint.check_device( self, device )
def setup_part2(self): '''Initializes the basic and optional services/devices and the enabled plugins (backends).''' self.setup_ssdp_server() if not self.ssdp_server: raise Exception('Unable to initialize an ssdp server') self.msearch = MSearch(self.ssdp_server, test=self.is_unittest) reactor.addSystemEventTrigger( 'before', 'shutdown', self.shutdown, force=True, ) self.setup_web_server() if not self.urlbase: raise Exception('Unable to initialize an web server') self.setup_plugins() # Control Point Initialization if (self.config.get('controlpoint', 'no') == 'yes' or self.config.get('json', 'no') == 'yes'): self.ctrl = ControlPoint(self) # Json Interface Initialization if self.config.get('json', 'no') == 'yes': from coherence.json_service import JsonInterface self.json = JsonInterface(self.ctrl) # Transcoder Initialization if self.config.get('transcoding', 'no') == 'yes': from coherence.transcoder import TranscoderManager self.transcoder_manager = TranscoderManager(self) # DBus Initialization if self.config.get('use_dbus', 'no') == 'yes': try: from coherence import dbus_service if self.ctrl is None: self.ctrl = ControlPoint(self) self.ctrl.auto_client_append('InternetGatewayDevice') self.dbus = dbus_service.DBusPontoon(self.ctrl) except Exception as msg: self.warning(f'Unable to activate dbus sub-system: {msg}') self.debug(traceback.format_exc())
def __init__(self,command,config): #print "command %r %r %r" %(command,config,config.subOptions) self.config = config self.locked = False if command == None: self.command = 'show-devices' else: self.command = command if self.command == 'show-devices': self.locked = None if self.command == 'add-mapping': if self.config.subOptions['internal-host'] == None: raise Exception("internal-host parameter missing") if self.config.subOptions['internal-port'] == None: raise Exception("internal-port parameter missing") if self.config.subOptions['protocol'].lower() not in ['tcp','udp']: raise Exception("protocol value invalid") if self.config.subOptions['active'].lower() in ['y','true','1','yes']: self.config.subOptions['active'] = True else: self.config.subOptions['active'] = False if self.config.subOptions['remote-host'].lower() in ['""','any']: self.config.subOptions['remote-host'] = '' if self.config.subOptions['external-port'] == None: self.config.subOptions['external-port'] = self.config.subOptions['internal-port'] if self.command == 'delete-mapping': if self.config.subOptions['remote-host'] == None: raise Exception("remote-host parameter missing") if self.config.subOptions['external-port'] == None: raise Exception("external-port parameter missing") if self.config.subOptions['protocol'].lower() not in ['tcp','udp']: raise Exception("protocol value invalid") coherence_config = {} coherence_config['logmode'] = 'none' self.control_point = ControlPoint(Coherence(coherence_config),auto_client=['InternetGatewayDevice']) self.control_point.connect(self.igd_found, 'Coherence.UPnP.ControlPoint.InternetGatewayDevice.detected') self.control_point.connect(self.igd_removed, 'Coherence.UPnP.ControlPoint.InternetGatewayDevice.removed') self.timeout = reactor.callLater(int(self.config['timeout']),self.stop) self.devices = {} self.reports = {'show-devices': self.show_devices, 'show-mappings': self.show_mappings, 'info': self.show_info}
def __init__(self): # configuration setup self.gconf = gconf.client_get_default() self.conf = { "autoconnect": self.gconf.get_bool(gconf_keys["autoconnect"]), "udn": self.gconf.get_string(gconf_keys["udn"]), "mmkeys": self.gconf.get_bool(gconf_keys["mmkeys"]), } # coherence setup self.coherence = Coherence({"logmode": "warning"}) self.ctp = ControlPoint(self.coherence, auto_client=["MediaRenderer"]) # internals setup self.client = MediaRendererClient(self.coherence) self.gui = StatusIconController(self) self.gui.connect(self.client) self.mmkeys = MMKeysController(name="MUPnPApp") # hack to make it start if True or self.conf["mmkeys"]: self.mmkeys.connect(self.client) # signal connection self.ctp.connect(self._renderer_found, "Coherence.UPnP.ControlPoint.MediaRenderer.detected") self.ctp.connect(self._renderer_removed, "Coherence.UPnP.ControlPoint.MediaRenderer.removed")
def doStart(*args, **kwargs): if self._controlPoint: Log.w("already running!") return Log.i("starting now!") self._startPending = False self.coherence = Coherence({ 'logging': { 'level' : 'warning', 'subsystem' : [ {'name' : 'msearch', 'level' : 'warning'}, {'name' : 'ssdp', 'level' : 'warning'} ]} }) self._controlPoint = ControlPoint(self.coherence, auto_client=['MediaServer','MediaRenderer']) self.coherence.ctrl = self._controlPoint self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self._controlPoint.connect(self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.connect(self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.connect(self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.connect(self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.connect(self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self._controlPoint.connect(self._onMediaDeviceRemoved, 'Coherence.UPnP.RootDevice.removed') self.__deferredShutDown = None if self._session: self._callPlugins(reason=0)
def doStart(*args, **kwargs): if self._controlPoint: Log.w("already running!") return Log.i("starting now!") self._startPending = False self.coherence = Coherence({ 'logging': { 'level': 'warning', 'subsystem': [{ 'name': 'msearch', 'level': 'warning' }, { 'name': 'ssdp', 'level': 'warning' }] } }) self._controlPoint = ControlPoint( self.coherence, auto_client=['MediaServer', 'MediaRenderer']) self.coherence.ctrl = self._controlPoint self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self._controlPoint.connect( self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.connect( self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.connect( self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.connect( self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.connect( self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self._controlPoint.connect(self._onMediaDeviceRemoved, 'Coherence.UPnP.RootDevice.removed') self.__deferredShutDown = None if self._session: self._callPlugins(reason=0)
def start(): control_point = ControlPoint(Coherence({'logmode': 'warning'}), auto_client=['MediaServer']) control_point.connect(media_server_found, 'Coherence.UPnP.ControlPoint.MediaServer.detected') control_point.connect(media_server_removed, 'Coherence.UPnP.ControlPoint.MediaServer.removed')
def start(self): print "Upnp backend starting" # clear device list self._removeXML() # config Coherence conf = Config(self._configfile) c = Coherence({'logmode': 'warning'}) c.setup(conf) #add share folders con = "" for f in config.share_folders: if len(con) > 0: con += "," con += f c.add_plugin('FSStore', name=config.device_name, content=con) # start a ControlPoint to start detecting upnp activity cp = ControlPoint(c, auto_client=['MediaServer']) cp.connect(self.mediaserver_detected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') cp.connect(self.mediaserver_removed, 'Coherence.UPnP.ControlPoint.MediaServer.removed') #self._localdevice = cp.get_device_by_name(self._localdevicename) #print "SPRURPDDSOPF ",self._localdevice.client.get_friendly_name() print "Upnp loop succesfully started" self._handler.start() print "DBus loop succesfully started"
def start(): control_point = ControlPoint(Coherence({'logmode': 'warning'}), auto_client=['MediaServer']) control_point.connect(media_server_found, 'Coherence.UPnP.ControlPoint.MediaServer.detected') control_point.connect(media_server_removed, 'Coherence.UPnP.ControlPoint.MediaServer.removed') # now we should also try to discover the ones that are already there: for device in control_point.coherence.devices: print device
def __init__(self): self.browse_count = 0 self.music = Music_DB() self.__client = None self.control_point = ControlPoint(Coherence({'logmode':'warning'}), auto_client=['MediaServer']) self.control_point.connect(self.media_server_found, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self.control_point.connect(self.media_server_removed, 'Coherence.UPnP.ControlPoint.MediaServer.removed') # now we should also try to discover the ones that are already there: for device in self.control_point.coherence.devices: print device
def __init__(self): self.coherence = Coherence({'logmode':'warning'}) self._controlPoint = ControlPoint(self.coherence, auto_client=['MediaServer','MediaRenderer']) self._controlPoint.connect(self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.connect(self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.connect(self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.connect(self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.connect(self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__browser = [] self.__devices = [] self.onMediaServerDetected = [] self.onMediaServerRemoved = [] self.onMediaRendererDetected = [] self.onMediaRendererRemoved = [] self.onMediaDeviceDectected = []
def setup_part2(self): self.info('running on host: %s', self.hostname) if self.hostname.startswith('127.'): self.warning('detection of own ip failed, using %s as own address, functionality will be limited', self.hostname) unittest = self.config.get('unittest', 'no') unittest = False if unittest == 'no' else True """ SSDP Server Initialization """ try: # TODO: add ip/interface bind self.ssdp_server = SSDPServer(test=unittest) except CannotListenError as err: self.error("Error starting the SSDP-server: %s", err) self.debug("Error starting the SSDP-server", exc_info=True) reactor.stop() return louie.connect(self.create_device, 'Coherence.UPnP.SSDP.new_device', louie.Any) louie.connect(self.remove_device, 'Coherence.UPnP.SSDP.removed_device', louie.Any) louie.connect(self.add_device, 'Coherence.UPnP.RootDevice.detection_completed', louie.Any) # louie.connect( self.receiver, 'Coherence.UPnP.Service.detection_completed', louie.Any) self.ssdp_server.subscribe("new_device", self.add_device) self.ssdp_server.subscribe("removed_device", self.remove_device) self.msearch = MSearch(self.ssdp_server, test=unittest) reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, force=True) """ Web Server Initialization """ try: # TODO: add ip/interface bind self.web_server = WebServer(self.config.get('web-ui', None), self.web_server_port, self) except CannotListenError: self.warning('port %r already in use, aborting!', self.web_server_port) reactor.stop() return self.urlbase = 'http://%s:%d/' % (self.hostname, self.web_server_port) # self.renew_service_subscription_loop = task.LoopingCall(self.check_devices) # self.renew_service_subscription_loop.start(20.0, now=False) try: plugins = self.config['plugin'] if isinstance(plugins, dict): plugins = [plugins] except: plugins = None if plugins is None: plugins = self.config.get('plugins', None) if plugins is None: self.info("No plugin defined!") else: if isinstance(plugins, dict): for plugin, arguments in list(plugins.items()): try: if not isinstance(arguments, dict): arguments = {} self.add_plugin(plugin, **arguments) except Exception as msg: self.warning("Can't enable plugin, %s: %s!", plugin, msg) self.info(traceback.format_exc()) else: for plugin in plugins: try: if plugin['active'] == 'no': continue except (KeyError, TypeError): pass try: backend = plugin['backend'] arguments = copy.copy(plugin) del arguments['backend'] backend = self.add_plugin(backend, **arguments) if self.writeable_config(): if 'uuid' not in plugin: plugin['uuid'] = str(backend.uuid)[5:] self.config.save() except Exception as msg: self.warning("Can't enable plugin, %s: %s!", plugin, msg) self.info(traceback.format_exc()) self.external_address = ':'.join((self.hostname, str(self.web_server_port))) """ Control Point Initialization """ if self.config.get('controlpoint', 'no') == 'yes' or self.config.get('json', 'no') == 'yes': self.ctrl = ControlPoint(self) """ Json Interface Initialization """ if self.config.get('json', 'no') == 'yes': from coherence.json_service import JsonInterface self.json = JsonInterface(self.ctrl) """ Transcoder Initialization """ if self.config.get('transcoding', 'no') == 'yes': from coherence.transcoder import TranscoderManager self.transcoder_manager = TranscoderManager(self) """ DBus Initialization """ if self.config.get('use_dbus', 'no') == 'yes': try: from coherence import dbus_service if self.ctrl is None: self.ctrl = ControlPoint(self) self.ctrl.auto_client_append('InternetGatewayDevice') self.dbus = dbus_service.DBusPontoon(self.ctrl) except Exception as msg: self.warning("Unable to activate dbus sub-system: %r", msg) self.debug(traceback.format_exc())
def notify_device( self, client, device, isComplete=False ): if self.is_visible_device( device ): ControlPoint.notify_device( self, client, device, isComplete ) else: self.info("Hiding %s from application", device)
class ManagedControlPoint(object): DEVICE_TYPE_SATIP_SERVER = "SatIPServer" DEVICE_TYPE_DREAMBOX = "Dreambox" URI_BASE_DREAMBOX = "urn:dreambox-de:device" def __init__(self): self.coherence = None self._controlPoint = None self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self.onMediaServerDetected = [] self.onMediaServerRemoved = [] self.onMediaRendererDetected = [] self.onMediaRendererRemoved = [] self.onMediaDeviceDectected = [] self.onMediaDeviceRemoved = [] self.onSatIpServerDetected = [] self.onSatIpServerRemoved = [] self.onDreamboxDetected = [] self.onDreamboxRemoved = [] self._session = None self.__deferredShutDown = None self._startPending = False def _onShutdownFinished(self, *args, **kwargs): self.__deferredShutDown = None if self._startPending: self.start() def start(self): def doStart(*args, **kwargs): if self._controlPoint: Log.w("already running!") return Log.i("starting now!") self._startPending = False self.coherence = Coherence({ 'logging': { 'level': 'warning', 'subsystem': [{ 'name': 'msearch', 'level': 'warning' }, { 'name': 'ssdp', 'level': 'warning' }] } }) self._controlPoint = ControlPoint( self.coherence, auto_client=['MediaServer', 'MediaRenderer']) self.coherence.ctrl = self._controlPoint self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self._controlPoint.connect( self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.connect( self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.connect( self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.connect( self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.connect( self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self._controlPoint.connect(self._onMediaDeviceRemoved, 'Coherence.UPnP.RootDevice.removed') self.__deferredShutDown = None if self._session: self._callPlugins(reason=0) if self.__deferredShutDown: Log.w("deferring start until shutdown is finished") if not self._startPending: self._startPending = True else: doStart() def restart(self): Log.i() if not self.__deferredShutDown: self.shutdown() self.start() def setSession(self, session): self._session = session if self.coherence: self._callPlugins(reason=0) def _callPlugins(self, reason=0): for plugin in plugins.getPlugins(PluginDescriptor.WHERE_UPNP): plugin(reason, session=self._session) def _onMediaServerDetected(self, client, udn): print "[DLNA] MediaServer Detected: %s (%s)" % ( client.device.get_friendly_name(), client.device.get_friendly_device_type()) self.__mediaServerClients[udn] = client for fnc in self.onMediaServerDetected: fnc(udn, client) def _onMediaServerRemoved(self, udn): if self.__mediaServerClients.get(udn, None) != None: del self.__mediaServerClients[udn] for fnc in self.onMediaServerRemoved: fnc(udn) def _onMediaRendererDetected(self, client, udn): print "[DLNA] MediaRenderer detected: %s (%s, %s)" % ( client.device.get_friendly_name(), client.device.get_friendly_device_type(), udn) self.__mediaRendererClients[udn] = client for fnc in self.onMediaRendererDetected: fnc(udn, client) def _onMediaRendererRemoved(self, udn): print "[DLNA] MediaRenderer removed: %s" % (udn) if self.__mediaRendererClients.get(udn, None) != None: del self.__mediaRendererClients[udn] for fnc in self.onMediaRendererRemoved: fnc(udn) def _onMediaDeviceDectected(self, device): if device.udn in self.__mediaDevices: return self.__mediaDevices[device.udn] = device device_type = device.get_friendly_device_type() if device_type == self.DEVICE_TYPE_SATIP_SERVER: Log.i("New SAT>IP Server found: %s (%s - %s)" % (device.get_friendly_name(), device.get_friendly_device_type(), device.get_satipcap())) for fnc in self.onSatIpServerDetected: fnc(device) elif device_type == self.DEVICE_TYPE_DREAMBOX: Log.i( "New Dreambox found: %s (%s - %s)" % (device.get_friendly_name(), device.get_friendly_device_type(), device.get_presentation_url())) for fnc in self.onDreamboxDetected: fnc(device) else: Log.i("New Device found: %s (%s)" % (device.get_friendly_name(), device.get_friendly_device_type())) def _onMediaDeviceRemoved(self, usn): if usn in self.__mediaDevices: print "[DLNA] Device removed: %s" % (usn) device = self.__mediaDevices[usn] device_type = device.get_friendly_device_type() if device_type == self.DEVICE_TYPE_SATIP_SERVER: for fnc in self.onSatIpServerRemoved: fnc(device) elif device_type == self.DEVICE_TYPE_DREAMBOX: for fnc in self.onDreamboxRemoved: fnc(device) for fnc in self.onMediaDeviceRemoved: fnc(device) del self.__mediaDevices[usn] def registerRenderer(self, classDef, **kwargs): renderer = MediaRenderer(self.coherence, classDef, no_thread_needed=True, **kwargs) self.__devices.append(renderer) return renderer def registerServer(self, classDef, **kwargs): server = MediaServer(self.coherence, classDef, no_thread_needed=True, **kwargs) self.__devices.append(server) return server def registerDevice(self, instance, **kwargs): self.__devices.append(instance) return instance def getServerList(self): return self.__mediaServerClients.values() def getRenderingControlClientList(self): return self.__mediaRendererClients.values() def getDeviceName(self, client): return Item.ue(client.device.get_friendly_name()) def getSatIPDevices(self): devices = [] for device in self.__mediaDevices.itervalues(): if device.get_friendly_device_type( ) == self.DEVICE_TYPE_SATIP_SERVER: devices.append(device) return devices def getDreamboxes(self): devices = [] for device in self.__mediaDevices.itervalues(): if device.get_friendly_device_type() == self.DEVICE_TYPE_DREAMBOX: devices.append(device) return devices def getDevice(self, uuid): for device in self.__devices: if device.uuid == uuid: return device return None def removeDevice(self, uuid): device = self.getDevice(uuid) if device: device.unregister() self.__devices.remove(device) return True return False def shutdown(self): Log.i("%s" % (self.coherence, )) if True: Log.w( "shutdown is broken... will continue running. please restart enigma2 instead!" ) return if self.coherence: self._callPlugins(reason=1) self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self.__deferredShutDown = self.coherence.shutdown(force=True) self.__deferredShutDown.addCallback(self._onShutdownFinished) self._controlPoint.disconnect( self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.disconnect( self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.disconnect( self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.disconnect( self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.disconnect( self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self._controlPoint.disconnect(self._onMediaDeviceRemoved, 'Coherence.UPnP.RootDevice.removed') self.coherence = None self._controlPoint = None
class ManagedControlPoint(object): def __init__(self): self.coherence = Coherence({'logmode':'warning'}) self._controlPoint = ControlPoint(self.coherence, auto_client=['MediaServer','MediaRenderer']) self._controlPoint.connect(self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.connect(self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.connect(self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.connect(self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.connect(self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__browser = [] self.__devices = [] self.onMediaServerDetected = [] self.onMediaServerRemoved = [] self.onMediaRendererDetected = [] self.onMediaRendererRemoved = [] self.onMediaDeviceDectected = [] def _onMediaServerDetected(self, client, udn): print "[DLNA] MediaServer Detected: %s (%s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type()) self.__mediaServerClients[udn] = client for fnc in self.onMediaServerDetected: fnc(udn, client) def _onMediaServerRemoved(self, udn): if self.__mediaServerClients.get(udn, None) != None: del self.__mediaServerClients[udn] for fnc in self.onMediaServerRemoved: fnc(udn) def _onMediaRendererDetected(self, client, udn): print "[DLNA] MediaRenderer detected: %s (%s, %s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type(), udn) self.__mediaRendererClients[udn] = client for fnc in self.onMediaRendererDetected: fnc(udn, client) def _onMediaRendererRemoved(self, udn): print "[DLNA] MediaRenderer removed: %s" % (udn) if self.__mediaRendererClients.get(udn, None) != None: del self.__mediaRendererClients[udn] for fnc in self.onMediaRendererRemoved: fnc(udn) def _onMediaDeviceDectected(self, device): print "[DLNA] Device found: %s (%s)" % (device.get_friendly_name(), device.get_friendly_device_type()) self.__mediaDevices[device.udn] = device def registerRenderer(self, classDef, **kwargs): renderer = MediaRenderer(self.coherence, classDef, no_thread_needed=True, **kwargs) self.__devices.append(renderer) return renderer def registerServer(self, classDef, **kwargs): server = MediaServer(self.coherence, classDef, no_thread_needed=True, **kwargs) self.__devices.append(server) return server def getServerList(self): return self.__mediaServerClients.values() def getRenderingControlClientList(self): return self.__mediaRendererClients.values() def getDeviceName(self, client): return Item.ue(client.device.get_friendly_name()) def shutdown(self): for device in self.__devices: device.unregister() self._controlPoint.shutdown()
def __init__(self, logfile=None): config = {'logmode': 'none', 'logfile': logfile} self.coherence = Coherence(config) self.controlpoint = ControlPoint(self.coherence, auto_client=[]) window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.connect("delete_event", lambda x, y: reactor.stop()) window.set_default_size(350, 700) window.set_title('UPnP Inspector') icon = resource_filename(__name__, os.path.join('icons', 'inspector-icon.png')) gtk.window_set_default_icon_from_file(icon) vbox = gtk.VBox(homogeneous=False, spacing=0) menu_bar = gtk.MenuBar() menu = gtk.Menu() refresh_item = gtk.MenuItem("Rediscover Devices") refresh_item.connect("activate", self.refresh_devices) menu.append(refresh_item) menu.append(gtk.SeparatorMenuItem()) quit_item = gtk.MenuItem("Quit") menu.append(quit_item) quit_item.connect("activate", lambda x: reactor.stop()) file_menu = gtk.MenuItem("File") file_menu.set_submenu(menu) menu_bar.append(file_menu) menu = gtk.Menu() self.show_details_item = gtk.CheckMenuItem("show details") menu.append(self.show_details_item) self.show_details_item.connect("activate", self.show_details_widget, "view.details") self.show_events_item = gtk.CheckMenuItem("show events") menu.append(self.show_events_item) self.show_events_item.connect("activate", self.show_events_widget, "view.events") self.show_log_item = gtk.CheckMenuItem("show global log") menu.append(self.show_log_item) self.show_log_item.connect("activate", self.show_log_widget, "view.log") #self.show_log_item.set_sensitive(False) view_menu = gtk.MenuItem("View") view_menu.set_submenu(menu) menu_bar.append(view_menu) test_menu = gtk.MenuItem("Test") test_menu.set_sensitive(False) #test_menu.set_submenu(menu) menu_bar.append(test_menu) menu = gtk.Menu() item = gtk.MenuItem("Info") menu.append(item) item.connect("activate", self.show_about_widget, "help.info") help_menu = gtk.MenuItem("Help") help_menu.set_submenu(menu) menu_bar.append(help_menu) vbox.pack_start(menu_bar, False, False, 2) self.device_tree = DevicesWidget(self.coherence) self.device_tree.cb_item_left_click = self.show_details vbox.pack_start(self.device_tree.window, True, True, 0) window.add(vbox) window.show_all() self.events_widget = EventsWidget(self.coherence) self.events_widget.window.connect('delete_event', self.hide_events_widget) self.details_widget = DetailsWidget(self.coherence) self.details_widget.window.connect('delete_event', self.hide_details_widget) self.log_widget = LogWidget(self.coherence) self.log_widget.window.connect('delete_event', self.hide_log_widget)
class Puncher(object): def __init__(self,command,config): #print "command %r %r %r" %(command,config,config.subOptions) self.config = config self.locked = False if command == None: self.command = 'show-devices' else: self.command = command if self.command == 'show-devices': self.locked = None if self.command == 'add-mapping': if self.config.subOptions['internal-host'] == None: raise Exception("internal-host parameter missing") if self.config.subOptions['internal-port'] == None: raise Exception("internal-port parameter missing") if self.config.subOptions['protocol'].lower() not in ['tcp','udp']: raise Exception("protocol value invalid") if self.config.subOptions['active'].lower() in ['y','true','1','yes']: self.config.subOptions['active'] = True else: self.config.subOptions['active'] = False if self.config.subOptions['remote-host'].lower() in ['""','any']: self.config.subOptions['remote-host'] = '' if self.config.subOptions['external-port'] == None: self.config.subOptions['external-port'] = self.config.subOptions['internal-port'] if self.command == 'delete-mapping': if self.config.subOptions['remote-host'] == None: raise Exception("remote-host parameter missing") if self.config.subOptions['external-port'] == None: raise Exception("external-port parameter missing") if self.config.subOptions['protocol'].lower() not in ['tcp','udp']: raise Exception("protocol value invalid") coherence_config = {} coherence_config['logmode'] = 'none' self.control_point = ControlPoint(Coherence(coherence_config),auto_client=['InternetGatewayDevice']) self.control_point.connect(self.igd_found, 'Coherence.UPnP.ControlPoint.InternetGatewayDevice.detected') self.control_point.connect(self.igd_removed, 'Coherence.UPnP.ControlPoint.InternetGatewayDevice.removed') self.timeout = reactor.callLater(int(self.config['timeout']),self.stop) self.devices = {} self.reports = {'show-devices': self.show_devices, 'show-mappings': self.show_mappings, 'info': self.show_info} def show_devices(self): for uuid in self.devices.keys(): print "%s with uuid:%s" % (self.devices[uuid]['friendly_name'], uuid) def show_info(self): for uuid in self.devices.keys(): print "%s with uuid:%s" % (self.devices[uuid]['friendly_name'], uuid) if len(self.devices) > 0: print "External IP address: ", self.external_ip_address print "Number of port-mappings: ", self.port_mapping_number_of_entries def show_mappings(self): for uuid in self.devices.keys(): print "%s with uuid:%s" % (self.devices[uuid]['friendly_name'], uuid) mappings = self.devices[uuid].get('mappings',None) if mappings == None or len(mappings) == 0: print "no port-mappings found" else: print "Ext. Port | Remote Host | Int. Port | Internal Host | Prot. | active | duration | description" print "=" * 100 for mapping in mappings: if mapping['NewLeaseDuration'] == '0': mapping['NewLeaseDuration'] = 'infinite' else: mapping['NewLeaseDuration'] += 'sec' if mapping['NewRemoteHost'] == '': mapping['NewRemoteHost'] = 'any' if mapping['NewEnabled'] == '1': mapping['NewEnabled'] = 'yes' else: mapping['NewEnabled'] = 'no' print " %05s | %-14s | %05s | %-14s | %5s | %6s | %8s | %s" % (mapping['NewExternalPort'], mapping['NewRemoteHost'], mapping['NewInternalPort'], mapping['NewInternalClient'], mapping['NewProtocol'], mapping['NewEnabled'], mapping['NewLeaseDuration'], mapping['NewPortMappingDescription']) print "=" * 100 def stop(self,quiet=False): try: self.timeout.cancel() except: pass if quiet == False: if len(self.devices) == 0: print "no InternetGatewayDevice found" elif len(self.devices) == 1: print "1 InternetGatewayDevice found:" else: print "%d InternetGatewayDevices found:" % len(self.devices) self.reports.get(self.command,self.show_devices)() print "" reactor.stop() def append_mappings(self,mappings,device): device['mappings'] = mappings self.stop() def add_mapping_ok(self,result,device): print "port-mapping to %s added" %device['friendly_name'] self.stop(quiet=True) def add_mapping_failed(self,result,device): print "failed to add port-mapping to %s" %device['friendly_name'] self.stop(quiet=True) def delete_mapping_ok(self,result,device): print "port-mapping deleted from %s" %device['friendly_name'] self.stop(quiet=True) def delete_mapping_failed(self,result,device): print "failed to delete port-mapping from %s" %device['friendly_name'] self.stop(quiet=True) def igd_found(self,client,udn): #print "IGD found", client.device.get_friendly_name() if self.locked == True: return elif self.locked == False: self.locked = True if(self.config['uuid'] != None and client.device.get_uuid().endswith(self.config['uuid']) == False): return self.devices[client.device.get_uuid()] = {'friendly_name': client.device.get_friendly_name()} if self.locked == True: wan_ip_connection_service = client.wan_device.wan_connection_device.wan_ip_connection or \ client.wan_device.wan_connection_device.wan_ppp_connection if self.command == 'show-mappings': dfr = wan_ip_connection_service.get_all_port_mapping_entries() dfr.addCallback(self.append_mappings,self.devices[client.device.get_uuid()]) elif self.command == 'add-mapping': dfr = wan_ip_connection_service.add_port_mapping(remote_host=self.config.subOptions['remote-host'], external_port=int(self.config.subOptions['external-port']), protocol=self.config.subOptions['protocol'].upper(), internal_port=int(self.config.subOptions['internal-port']), internal_client=self.config.subOptions['internal-host'], enabled=self.config.subOptions['active'], port_mapping_description=self.config.subOptions['description'], lease_duration=int(self.config.subOptions['lease-duration'])) dfr.addCallback(self.add_mapping_ok,self.devices[client.device.get_uuid()]) dfr.addErrback(self.add_mapping_failed,self.devices[client.device.get_uuid()]) elif self.command == 'delete-mapping': dfr = wan_ip_connection_service.delete_port_mapping(remote_host=self.config.subOptions['remote-host'], external_port=int(self.config.subOptions['external-port']), protocol=self.config.subOptions['protocol'].upper()) dfr.addCallback(self.delete_mapping_ok,self.devices[client.device.get_uuid()]) dfr.addErrback(self.delete_mapping_failed,self.devices[client.device.get_uuid()]) elif self.command == 'info': self.port_mapping_number_of_entries = None self.external_ip_address = None wan_ip_connection_service.subscribe_for_variable('PortMappingNumberOfEntries', callback=self.state_variable_change) wan_ip_connection_service.subscribe_for_variable('ExternalIPAddress', callback=self.state_variable_change) def igd_removed(self,udn): #print "IGD removed", udn pass def state_variable_change(self,variable): if variable.name == 'ExternalIPAddress': self.external_ip_address = variable.value elif variable.name == 'PortMappingNumberOfEntries': if variable.value != '': self.port_mapping_number_of_entries = int(variable.value) else: self.port_mapping_number_of_entries = 0 if(self.port_mapping_number_of_entries != None and self.external_ip_address != None): self.stop()
class UpnpRapp(object): def __init__(self): # configuration setup self.gconf = gconf.client_get_default() self.conf = { "autoconnect": self.gconf.get_bool(gconf_keys["autoconnect"]), "udn": self.gconf.get_string(gconf_keys["udn"]), "mmkeys": self.gconf.get_bool(gconf_keys["mmkeys"]), } # coherence setup self.coherence = Coherence({"logmode": "warning"}) self.ctp = ControlPoint(self.coherence, auto_client=["MediaRenderer"]) # internals setup self.client = MediaRendererClient(self.coherence) self.gui = StatusIconController(self) self.gui.connect(self.client) self.mmkeys = MMKeysController(name="MUPnPApp") # hack to make it start if True or self.conf["mmkeys"]: self.mmkeys.connect(self.client) # signal connection self.ctp.connect(self._renderer_found, "Coherence.UPnP.ControlPoint.MediaRenderer.detected") self.ctp.connect(self._renderer_removed, "Coherence.UPnP.ControlPoint.MediaRenderer.removed") def _renderer_found(self, device=None, udn=None, client=None): # FIXME: take care about the auto connect if not connected yet # instead of just connecting if not device: device = self.coherence.get_device_with_id(udn) self.gui.device_found(device, udn) if not self.client.device and self.conf["autoconnect"]: if device.udn == self.conf["udn"]: self.connect(device) def _renderer_removed(self, device=None, udn=None): return self.gui.renderer_removed(device, udn) def set_autoconnect(self, value): self.conf["autoconnect"] = value self.gconf.set_bool(gconf_keys["autoconnect"], value) def set_mmkeys(self, value): self.conf["mmkeys"] = value self.gconf.set_bool(gconf_keys["mmkeys"], value) # reloading if not value: self.mmkeys.disconnect() else: self.mmkeys.connect(self.client) def quit(self, value=0): # FIXME: we should disconnect and stuff here sys.exit(value) def connect(self, device): print "connecting to %s" % device.get_friendly_name() self.gconf.set_string(gconf_keys["udn"], str(device.udn)) self.client.disconnect() self.client.connect(device) # HACK! self.gui._connection_state_changed(True, device)
from twisted.internet import stdio from twisted.internet import defer from twisted.protocols import basic from lxml import etree import time, json, shutil, os from coherence.base import Coherence from coherence.upnp.devices.control_point import ControlPoint logfile = None config = {'logmode': 'none', 'logfile': logfile} coherence = Coherence(config) controlpoint = ControlPoint(coherence, auto_client=[]) BOOKMARKPATH = os.path.expanduser('~/.grace-bookmarks') devices = [] unknown_devices = [] CONFPATH = os.path.join(os.path.expanduser('~'), '.graceradiorc') DEFAULT_CONFIG = { # Number of lines to output from the buffer every time enter is pressed "buffer_rate": 20, } CONFIG = load_config(DEFAULT_CONFIG, CONFPATH)
class Coherence(log.Loggable): __instance = None # Singleton logCategory = 'coherence' def __new__(cls, *args, **kwargs): if cls.__instance is None: cls.__instance = super(Coherence, cls).__new__(cls) cls.__instance.__initialized = False cls.__instance.__incarnations = 0 cls.__instance.__cls = cls cls.__instance.__incarnations += 1 return cls.__instance def __init__(self, config=None): # initialize only once if self.__initialized: return self.__initialized = True # supers log.Loggable.__init__(self) self.config = config or {} self.devices = [] self.children = {} self._callbacks = {} self.active_backends = {} self.available_plugins = None self.external_address = None self.urlbase = None self.web_server_port = int(config.get('serverport', 0)) """ Services """ self.ctrl = None self.dbus = None self.json = None self.msearch = None self.ssdp_server = None self.transcoder_manager = None self.web_server = None """ initializes logsystem a COHEN_DEBUG environment variable overwrites all level settings here """ try: logmode = config.get('logging').get('level', 'warning') except (KeyError, AttributeError): logmode = config.get('logmode', 'warning') try: subsystems = config.get('logging')['subsystem'] if isinstance(subsystems, dict): subsystems = [subsystems] for subsystem in subsystems: try: if subsystem['active'] == 'no': continue except (KeyError, TypeError): pass self.info("setting log-level for subsystem %s to %s", subsystem['name'], subsystem['level']) logging.getLogger(subsystem['name'].lower()).setLevel(subsystem['level'].upper()) except (KeyError, TypeError): subsystem_log = config.get('subsystem_log', {}) for subsystem, level in list(subsystem_log.items()): logging.getLogger(subsystem.lower()).setLevel(level.upper()) try: logfile = config.get('logging').get('logfile', None) if logfile is not None: logfile = str(logfile) except (KeyError, AttributeError, TypeError): logfile = config.get('logfile', None) log.init(logfile, logmode.upper()) self.warning("Coherence UPnP framework version %s starting...", __version__) network_if = config.get('interface') if network_if: self.hostname = get_ip_address('%s' % network_if) else: try: self.hostname = socket.gethostbyname(socket.gethostname()) except socket.gaierror: self.warning("hostname can't be resolved, maybe a system misconfiguration?") self.hostname = '127.0.0.1' if self.hostname.startswith('127.'): """ use interface detection via routing table as last resort """ def catch_result(hostname): self.hostname = hostname self.setup_part2() d = defer.maybeDeferred(get_host_address) d.addCallback(catch_result) else: self.setup_part2() def clear(self): """ we do need this to survive multiple calls to Coherence during trial tests """ self.__cls.__instance = None def setup_part2(self): self.info('running on host: %s', self.hostname) if self.hostname.startswith('127.'): self.warning('detection of own ip failed, using %s as own address, functionality will be limited', self.hostname) unittest = self.config.get('unittest', 'no') unittest = False if unittest == 'no' else True """ SSDP Server Initialization """ try: # TODO: add ip/interface bind self.ssdp_server = SSDPServer(test=unittest) except CannotListenError as err: self.error("Error starting the SSDP-server: %s", err) self.debug("Error starting the SSDP-server", exc_info=True) reactor.stop() return louie.connect(self.create_device, 'Coherence.UPnP.SSDP.new_device', louie.Any) louie.connect(self.remove_device, 'Coherence.UPnP.SSDP.removed_device', louie.Any) louie.connect(self.add_device, 'Coherence.UPnP.RootDevice.detection_completed', louie.Any) # louie.connect( self.receiver, 'Coherence.UPnP.Service.detection_completed', louie.Any) self.ssdp_server.subscribe("new_device", self.add_device) self.ssdp_server.subscribe("removed_device", self.remove_device) self.msearch = MSearch(self.ssdp_server, test=unittest) reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, force=True) """ Web Server Initialization """ try: # TODO: add ip/interface bind self.web_server = WebServer(self.config.get('web-ui', None), self.web_server_port, self) except CannotListenError: self.warning('port %r already in use, aborting!', self.web_server_port) reactor.stop() return self.urlbase = 'http://%s:%d/' % (self.hostname, self.web_server_port) # self.renew_service_subscription_loop = task.LoopingCall(self.check_devices) # self.renew_service_subscription_loop.start(20.0, now=False) try: plugins = self.config['plugin'] if isinstance(plugins, dict): plugins = [plugins] except: plugins = None if plugins is None: plugins = self.config.get('plugins', None) if plugins is None: self.info("No plugin defined!") else: if isinstance(plugins, dict): for plugin, arguments in list(plugins.items()): try: if not isinstance(arguments, dict): arguments = {} self.add_plugin(plugin, **arguments) except Exception as msg: self.warning("Can't enable plugin, %s: %s!", plugin, msg) self.info(traceback.format_exc()) else: for plugin in plugins: try: if plugin['active'] == 'no': continue except (KeyError, TypeError): pass try: backend = plugin['backend'] arguments = copy.copy(plugin) del arguments['backend'] backend = self.add_plugin(backend, **arguments) if self.writeable_config(): if 'uuid' not in plugin: plugin['uuid'] = str(backend.uuid)[5:] self.config.save() except Exception as msg: self.warning("Can't enable plugin, %s: %s!", plugin, msg) self.info(traceback.format_exc()) self.external_address = ':'.join((self.hostname, str(self.web_server_port))) """ Control Point Initialization """ if self.config.get('controlpoint', 'no') == 'yes' or self.config.get('json', 'no') == 'yes': self.ctrl = ControlPoint(self) """ Json Interface Initialization """ if self.config.get('json', 'no') == 'yes': from coherence.json_service import JsonInterface self.json = JsonInterface(self.ctrl) """ Transcoder Initialization """ if self.config.get('transcoding', 'no') == 'yes': from coherence.transcoder import TranscoderManager self.transcoder_manager = TranscoderManager(self) """ DBus Initialization """ if self.config.get('use_dbus', 'no') == 'yes': try: from coherence import dbus_service if self.ctrl is None: self.ctrl = ControlPoint(self) self.ctrl.auto_client_append('InternetGatewayDevice') self.dbus = dbus_service.DBusPontoon(self.ctrl) except Exception as msg: self.warning("Unable to activate dbus sub-system: %r", msg) self.debug(traceback.format_exc()) def add_plugin(self, plugin, **kwargs): self.info("adding plugin %r", plugin) self.available_plugins = Plugins() # TODO clean up this exception concept try: plugin_class = self.available_plugins.get(plugin, None) if plugin_class is None: raise KeyError for device in plugin_class.implements: try: device_class = globals().get(device, None) if device_class is None: raise KeyError self.info("Activating %s plugin as %s...", plugin, device) new_backend = device_class(self, plugin_class, **kwargs) self.active_backends[str(new_backend.uuid)] = new_backend return new_backend except KeyError: self.warning("Can't enable %s plugin, sub-system %s not found!", plugin, device) except: self.exception("Can't enable %s plugin for sub-system %s", plugin, device) self.debug(traceback.format_exc()) except KeyError: self.warning("Can't enable %s plugin, not found!", plugin) except Exception as msg: self.warning("Can't enable %s plugin, %s!", plugin, msg) self.debug(traceback.format_exc()) def remove_plugin(self, plugin): """ Removes a backend from Coherence @:param plugin: is the object return by add_plugin or an UUID string """ if isinstance(plugin, str): try: plugin = self.active_backends[plugin] except KeyError: self.warning("no backend with the uuid %r found", plugin) return "" try: del self.active_backends[str(plugin.uuid)] self.info("removing plugin %r", plugin) plugin.unregister() return plugin.uuid except KeyError: self.warning("no backend with the uuid %r found", plugin.uuid) return "" @staticmethod def writeable_config(): """ do we have a new-style config file """ return False def store_plugin_config(self, uuid, items): """ find the backend with uuid and store in its the config the key and value pair(s) """ plugins = self.config.get('plugin') if plugins is None: self.warning("storing a plugin config option is only possible with the new config file format") return if isinstance(plugins, dict): plugins = [plugins] uuid = str(uuid) if uuid.startswith('uuid:'): uuid = uuid[5:] for plugin in plugins: try: if plugin['uuid'] == uuid: for k, v in list(items.items()): plugin[k] = v self.config.save() except: pass else: self.info("storing plugin config option for %s failed, plugin not found", uuid) def receiver(self, signal, *args, **kwargs): pass def shutdown(self, force=False): if self.__incarnations > 1 and not force: self.__incarnations -= 1 return if self.dbus: self.dbus.shutdown() self.dbus = None for backend in self.active_backends.values(): backend.unregister() self.active_backends = {} """ send service unsubscribe messages """ try: if self.web_server.port is not None: self.web_server.port.stopListening() self.web_server.port = None if hasattr(self.msearch, 'double_discover_loop'): self.msearch.double_discover_loop.stop() if hasattr(self.msearch, 'port'): self.msearch.port.stopListening() if hasattr(self.ssdp_server, 'resend_notify_loop'): self.ssdp_server.resend_notify_loop.stop() if hasattr(self.ssdp_server, 'port'): self.ssdp_server.port.stopListening() #self.renew_service_subscription_loop.stop() except: pass l = [] for root_device in self.get_devices(): for device in root_device.get_devices(): dd = device.unsubscribe_service_subscriptions() dd.addCallback(device.remove) l.append(dd) rd = root_device.unsubscribe_service_subscriptions() rd.addCallback(root_device.remove) l.append(rd) def homecleanup(result): """anything left over""" louie.disconnect(self.create_device, 'Coherence.UPnP.SSDP.new_device', louie.Any) louie.disconnect(self.remove_device, 'Coherence.UPnP.SSDP.removed_device', louie.Any) louie.disconnect(self.add_device, 'Coherence.UPnP.RootDevice.detection_completed', louie.Any) self.ssdp_server.shutdown() if self.ctrl: self.ctrl.shutdown() self.warning('Coherence UPnP framework shutdown') return result dl = defer.DeferredList(l) dl.addCallback(homecleanup) return dl def check_devices(self): """ iterate over devices and their embedded ones and renew subscriptions """ for root_device in self.get_devices(): root_device.renew_service_subscriptions() for device in root_device.get_devices(): device.renew_service_subscriptions() def subscribe(self, name, callback): self._callbacks.setdefault(name, []).append(callback) def unsubscribe(self, name, callback): callbacks = self._callbacks.get(name, []) if callback in callbacks: callbacks.remove(callback) self._callbacks[name] = callbacks def callback(self, name, *args): for callback in self._callbacks.get(name, []): callback(*args) def get_device_by_host(self, host): found = [] for device in self.devices: if device.get_host() == host: found.append(device) return found def get_device_with_usn(self, usn): found = None for device in self.devices: if device.get_usn() == usn: found = device break return found def get_device_with_id(self, device_id): found = None for device in self.devices: id = device.get_id() if device_id[:5] != 'uuid:': id = id[5:] if id == device_id: found = device break return found def get_devices(self): return self.devices def get_local_devices(self): return [d for d in self.devices if d.manifestation == 'local'] def get_nonlocal_devices(self): return [d for d in self.devices if d.manifestation == 'remote'] def create_device(self, device_type, infos): self.info("creating %s %s", infos['ST'], infos['USN']) if infos['ST'] == 'upnp:rootdevice': self.info("creating upnp:rootdevice %s", infos['USN']) root = RootDevice(infos) else: self.info("creating device/service %s", infos['USN']) root_id = infos['USN'][:-len(infos['ST']) - 2] root = self.get_device_with_id(root_id) # FIXME doesn't look like doing right thing device = Device(infos, root) def add_device(self, device): self.info("adding device %s %s %s", device.get_id(), device.get_usn(), device.friendly_device_type) self.devices.append(device) def remove_device(self, device_type, infos): self.info("removed device %s %s", infos['ST'], infos['USN']) device = self.get_device_with_usn(infos['USN']) if device: louie.send('Coherence.UPnP.Device.removed', None, usn=infos['USN']) self.devices.remove(device) device.remove() if infos['ST'] == 'upnp:rootdevice': louie.send('Coherence.UPnP.RootDevice.removed', None, usn=infos['USN']) self.callback("removed_device", infos['ST'], infos['USN']) def add_web_resource(self, name, sub): self.children[name] = sub def remove_web_resource(self, name): try: del self.children[name] except KeyError: """ probably the backend init failed """ pass @staticmethod def connect(receiver, signal=louie.signal.All, sender=louie.sender.Any, weak=True): """ wrapper method around louie.connect """ louie.connect(receiver, signal=signal, sender=sender, weak=weak) @staticmethod def disconnect(receiver, signal=louie.signal.All, sender=louie.sender.Any, weak=True): """ wrapper method around louie.disconnect """ louie.disconnect(receiver, signal=signal, sender=sender, weak=weak)
class ManagedControlPoint(object): DEVICE_TYPE_SATIP_SERVER = "SatIPServer" DEVICE_TYPE_DREAMBOX = "Dreambox" URI_BASE_DREAMBOX = "urn:dreambox-de:device" def __init__(self): self.coherence = None self._controlPoint = None self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self.onMediaServerDetected = [] self.onMediaServerRemoved = [] self.onMediaRendererDetected = [] self.onMediaRendererRemoved = [] self.onMediaDeviceDectected = [] self.onMediaDeviceRemoved = [] self.onSatIpServerDetected = [] self.onSatIpServerRemoved = [] self.onDreamboxDetected = [] self.onDreamboxRemoved = [] self._session = None self.__deferredShutDown = None self._startPending = False def _onShutdownFinished(self, *args, **kwargs): self.__deferredShutDown = None if self._startPending: self.start() def start(self): def doStart(*args, **kwargs): if self._controlPoint: Log.w("already running!") return Log.i("starting now!") self._startPending = False self.coherence = Coherence({ 'logging': { 'level' : 'warning', 'subsystem' : [ {'name' : 'msearch', 'level' : 'warning'}, {'name' : 'ssdp', 'level' : 'warning'} ]} }) self._controlPoint = ControlPoint(self.coherence, auto_client=['MediaServer','MediaRenderer']) self.coherence.ctrl = self._controlPoint self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self._controlPoint.connect(self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.connect(self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.connect(self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.connect(self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.connect(self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self._controlPoint.connect(self._onMediaDeviceRemoved, 'Coherence.UPnP.RootDevice.removed') self.__deferredShutDown = None if self._session: self._callPlugins(reason=0) if self.__deferredShutDown: Log.w("deferring start until shutdown is finished") if not self._startPending: self._startPending = True else: doStart() def restart(self): Log.i() if not self.__deferredShutDown: self.shutdown() self.start() def setSession(self, session): self._session = session if self.coherence: self._callPlugins(reason=0) def _callPlugins(self, reason=0): for plugin in plugins.getPlugins(PluginDescriptor.WHERE_UPNP): plugin(reason, session=self._session) def _onMediaServerDetected(self, client, udn): print "[DLNA] MediaServer Detected: %s (%s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type()) self.__mediaServerClients[udn] = client for fnc in self.onMediaServerDetected: fnc(udn, client) def _onMediaServerRemoved(self, udn): if self.__mediaServerClients.get(udn, None) != None: del self.__mediaServerClients[udn] for fnc in self.onMediaServerRemoved: fnc(udn) def _onMediaRendererDetected(self, client, udn): print "[DLNA] MediaRenderer detected: %s (%s, %s)" % (client.device.get_friendly_name(), client.device.get_friendly_device_type(), udn) self.__mediaRendererClients[udn] = client for fnc in self.onMediaRendererDetected: fnc(udn, client) def _onMediaRendererRemoved(self, udn): print "[DLNA] MediaRenderer removed: %s" % (udn) if self.__mediaRendererClients.get(udn, None) != None: del self.__mediaRendererClients[udn] for fnc in self.onMediaRendererRemoved: fnc(udn) def _onMediaDeviceDectected(self, device): if device.udn in self.__mediaDevices: return self.__mediaDevices[device.udn] = device device_type = device.get_friendly_device_type() if device_type == self.DEVICE_TYPE_SATIP_SERVER: Log.i("New SAT>IP Server found: %s (%s - %s)" %(device.get_friendly_name(), device.get_friendly_device_type(), device.get_satipcap())) for fnc in self.onSatIpServerDetected: fnc(device) elif device_type == self.DEVICE_TYPE_DREAMBOX: Log.i("New Dreambox found: %s (%s - %s)" %(device.get_friendly_name(), device.get_friendly_device_type(), device.get_presentation_url())) for fnc in self.onDreamboxDetected: fnc(device) else: Log.i("New Device found: %s (%s)" % (device.get_friendly_name(), device.get_friendly_device_type())) def _onMediaDeviceRemoved(self, usn): if usn in self.__mediaDevices: print "[DLNA] Device removed: %s" % (usn) device = self.__mediaDevices[usn] device_type = device.get_friendly_device_type() if device_type == self.DEVICE_TYPE_SATIP_SERVER: for fnc in self.onSatIpServerRemoved: fnc(device) elif device_type == self.DEVICE_TYPE_DREAMBOX: for fnc in self.onDreamboxRemoved: fnc(device) for fnc in self.onMediaDeviceRemoved: fnc(device) del self.__mediaDevices[usn] def registerRenderer(self, classDef, **kwargs): renderer = MediaRenderer(self.coherence, classDef, no_thread_needed=True, **kwargs) self.__devices.append(renderer) return renderer def registerServer(self, classDef, **kwargs): server = MediaServer(self.coherence, classDef, no_thread_needed=True, **kwargs) self.__devices.append(server) return server def registerDevice(self, instance, **kwargs): self.__devices.append(instance) return instance def getServerList(self): return self.__mediaServerClients.values() def getRenderingControlClientList(self): return self.__mediaRendererClients.values() def getDeviceName(self, client): return Item.ue(client.device.get_friendly_name()) def getSatIPDevices(self): devices = [] for device in self.__mediaDevices.itervalues(): if device.get_friendly_device_type() == self.DEVICE_TYPE_SATIP_SERVER: devices.append(device) return devices def getDreamboxes(self): devices = [] for device in self.__mediaDevices.itervalues(): if device.get_friendly_device_type() == self.DEVICE_TYPE_DREAMBOX: devices.append(device) return devices def getDevice(self, uuid): for device in self.__devices: if device.uuid == uuid: return device return None def removeDevice(self, uuid): device = self.getDevice(uuid) if device: device.unregister() self.__devices.remove(device) return True return False def shutdown(self): Log.i("%s" %(self.coherence,)) if True: Log.w("shutdown is broken... will continue running. please restart enigma2 instead!") return if self.coherence: self._callPlugins(reason=1) self.__mediaServerClients = {} self.__mediaRendererClients = {} self.__mediaDevices = {} self.__devices = [] self.__deferredShutDown = self.coherence.shutdown(force=True) self.__deferredShutDown.addCallback(self._onShutdownFinished) self._controlPoint.disconnect(self._onMediaServerDetected, 'Coherence.UPnP.ControlPoint.MediaServer.detected') self._controlPoint.disconnect(self._onMediaServerRemoved, 'Coherence.UPnP.ControlPoint.MediaServer.removed') self._controlPoint.disconnect(self._onMediaRendererDetected, 'Coherence.UPnP.ControlPoint.MediaRenderer.detected') self._controlPoint.disconnect(self._onMediaRendererRemoved, 'Coherence.UPnP.ControlPoint.MediaRenderer.removed') self._controlPoint.disconnect(self._onMediaDeviceDectected, 'Coherence.UPnP.Device.detection_completed') self._controlPoint.disconnect(self._onMediaDeviceRemoved, 'Coherence.UPnP.RootDevice.removed') self.coherence = None self._controlPoint = None
class Coherence(EventDispatcher, log.LogAble): ''' The Main class of the Cohen3 project. The Coherence class controls all the servers initialization depending on the configuration passed. It is also capable of initialize the plugins defined in config variable or by configuration file. It supports the creation of multiple servers at once. **Example of a simple server via plugin AppleTrailersStore**:: from coherence.base import Coherence from coherence.upnp.core.uuid import UUID from twisted.internet import reactor new_uuid = UUID() coherence = Coherence( {'logmode': 'info', 'controlpoint': 'yes', 'plugin': [{'backend': 'AppleTrailersStore', 'name': 'Cohen3 Example FSStore', 'uuid': new_uuid, } ] } ) reactor.run() .. versionchanged:: 0.9.0 * Introduced inheritance from EventDispatcher * The emitted events changed: - Coherence.UPnP.Device.detection_completed => coherence_device_detection_completed - Coherence.UPnP.Device.removed => coherence_device_removed - Coherence.UPnP.RootDevice.removed => coherence_root_device_removed * Changed some variables to benefit from the EventDispatcher's properties: - :attr:`devices` - :attr:`children` - :attr:`_callbacks` - :attr:`active_backends` - :attr:`ctrl` - :attr:`dbus` - :attr:`json` - :attr:`msearch` - :attr:`ssdp_server` - :attr:`transcoder_manager` - :attr:`web_server` ''' __instance = None # Singleton __initialized = False __incarnations = 0 __cls = None logCategory = 'coherence' devices = ListProperty([]) '''A list of the added devices.''' children = DictProperty({}) '''A dict containing the web resources.''' _callbacks = DictProperty({}) '''A dict containing the callbacks, used by the methods :meth:`subscribe` and :meth:`unsubscribe`.''' active_backends = DictProperty({}) '''A dict containing the active backends.''' # Services/Devices ctrl = Property(None) '''A coherence's instance of class :class:`~coherence.upnp.devices.control_point.ControlPoint`. This will be enabled if we request it by config dict or configuration file via keyword *controlpoint = yes*.''' dbus = Property(None) '''A coherence's instance of class :class:`~coherence.dbus_service.DBusPontoon`. This will be enabled if we request it by config dict or configuration file via keyword *use_dbus = yes*.''' json = Property(None) '''A coherence's instance of class :class:`~coherence.json_service.JsonInterface`. This will be enabled if we request it by config dict or configuration file via keyword *json = yes*.''' msearch = Property(None) '''A coherence's instance of class :class:`~coherence.upnp.core.msearch.MSearch`. This is automatically enabled when :class:`Coherence` is initialized''' ssdp_server = Property(None) '''A coherence's instance of class :class:`~coherence.upnp.core.ssdp.SSDPServer`. This is automatically enabled when :class:`Coherence` is initialized''' transcoder_manager = Property(None) '''A coherence's instance of class :class:`~coherence.transcoder.TranscoderManager`. This will be enabled if we request itby config dict or configuration file via keyword *transcoding = yes*.''' web_server = Property(None) '''A coherence's instance of class :class:`WebServer` or :class:`WebServerUi`. We can request our preference by config dict or configuration file. If we use the keyword *web-ui = yes*, then the class :class:`WebServerUi` will be used, otherwise, the enabled web server will be of class :class:`WebServer`.''' def __new__(cls, *args, **kwargs): if cls.__instance is None: cls.__instance = super(Coherence, cls).__new__(cls) cls.__instance.__initialized = False cls.__instance.__incarnations = 0 cls.__instance.__cls = cls cls.__instance.config = kwargs.get('config', {}) cls.__instance.__incarnations += 1 return cls.__instance def __init__(self, config=None): # initialize only once if self.__initialized: return self.__initialized = True # supers log.LogAble.__init__(self) EventDispatcher.__init__(self) self.register_event( 'coherence_device_detection_completed', 'coherence_device_removed', 'coherence_root_device_removed', ) self.config = config or {} self.available_plugins = None self.external_address = None self.urlbase = None self.web_server_port = int(config.get('serverport', 8080)) self.setup_logger() self.setup_hostname() if self.hostname.startswith('127.'): # use interface detection via routing table as last resort def catch_result(hostname): self.hostname = hostname self.setup_part2() d = defer.maybeDeferred(get_host_address) d.addCallback(catch_result) else: self.setup_part2() def clear(self): '''We do need this to survive multiple calls to Coherence during trial tests''' self.unbind_all() self.__cls.__instance = None @property def is_unittest(self): ''' Reads config and returns if we are testing or not via `unittest` key. ''' unittest = self.config.get('unittest', 'no') return False if unittest in {'no', False, None} else True @property def log_level(self): '''Read config and return the log level.''' try: log_level = self.config.get('logging').get('level', 'warning') except (KeyError, AttributeError): log_level = self.config.get('logmode', 'warning') return log_level.upper() @property def log_file(self): '''Read config and return the logfile or `None`.''' try: logfile = self.config.get('logging').get('logfile', None) if logfile is not None: logfile = str(logfile) except (KeyError, AttributeError, TypeError): logfile = self.config.get('logfile', None) return logfile def setup_logger(self): '''Initializes log's system based on config. .. note:: the COHEN_DEBUG environment variable overwrites all level settings in here ''' try: subsystems = self.config.get('logging')['subsystem'] if isinstance(subsystems, dict): subsystems = [subsystems] for subsystem in subsystems: try: if subsystem['active'] == 'no': continue except (KeyError, TypeError): pass self.info(f'setting log-level for subsystem ' + f'{subsystem["name"]} to {subsystem["level"]}') logging.getLogger(subsystem['name'].lower()).setLevel( subsystem['level'].upper()) except (KeyError, TypeError): subsystem_log = self.config.get('subsystem_log', {}) for subsystem, level in list(subsystem_log.items()): logging.getLogger(subsystem.lower()).setLevel(level.upper()) log.init(self.log_file, self.log_level) self.warning(f'Coherence UPnP framework version {__version__} ' + f'starting [log level: {self.log_level}]...') def setup_hostname(self): ''' Configure the `hostname`. .. note:: If something goes wrong will default to `127.0.0.1` ''' network_if = self.config.get('interface') if network_if: self.hostname = get_ip_address(f'{network_if}') else: try: self.hostname = socket.gethostbyname(socket.gethostname()) except socket.gaierror: self.warning('hostname can\'t be resolved, ' + 'maybe a system misconfiguration?') self.hostname = '127.0.0.1' self.info(f'running on host: {self.hostname}') if self.hostname.startswith('127.'): self.warning( f'detection of own ip failed, using {self.hostname} ' + f'as own address, functionality will be limited') def setup_ssdp_server(self): '''Initialize the :class:`~coherence.upnp.core.ssdp.SSDPServer`.''' try: # TODO: add ip/interface bind self.ssdp_server = SSDPServer(test=self.is_unittest) except CannotListenError as err: self.error(f'Error starting the SSDP-server: {err}') self.debug('Error starting the SSDP-server', exc_info=True) reactor.stop() return # maybe some devices are already notified, so we enforce # to create the device, if it is not already added...and # then we connect the signals for new detections. for st, usn in self.ssdp_server.root_devices: self.create_device(st, usn) self.ssdp_server.bind(new_device=self.create_device) self.ssdp_server.bind(removed_device=self.remove_device) self.ssdp_server.subscribe('new_device', self.add_device) self.ssdp_server.subscribe('removed_device', self.remove_device) def setup_web_server(self): '''Initialize the web server.''' try: # TODO: add ip/interface bind if self.config.get('web-ui', 'no') != 'yes': self.web_server = WebServer(None, self.web_server_port, self) else: self.web_server = WebServerUi(self.web_server_port, self, unittests=self.is_unittest) except CannotListenError: self.error( f'port {self.web_server_port} already in use, aborting!') reactor.stop() return self.urlbase = f'http://{self.hostname}:{self.web_server_port:d}/' self.external_address = ':'.join( (self.hostname, str(self.web_server_port))) # self.renew_service_subscription_loop = \ # task.LoopingCall(self.check_devices) # self.renew_service_subscription_loop.start(20.0, now=False) def setup_plugins(self): '''Initialize the plugins.''' try: plugins = self.config['plugin'] if isinstance(plugins, dict): plugins = [plugins] except Exception: plugins = None if plugins is None: plugins = self.config.get('plugins', None) if plugins is None: self.info('No plugin defined!') else: if isinstance(plugins, dict): for plugin, arguments in list(plugins.items()): try: if not isinstance(arguments, dict): arguments = {} self.add_plugin(plugin, **arguments) except Exception as msg: self.warning(f'Can\'t enable plugin, {plugin}: {msg}!') self.info(traceback.format_exc()) else: for plugin in plugins: try: if plugin['active'] == 'no': continue except (KeyError, TypeError): pass try: backend = plugin['backend'] arguments = copy.copy(plugin) del arguments['backend'] backend = self.add_plugin(backend, **arguments) if self.writeable_config(): if 'uuid' not in plugin: plugin['uuid'] = str(backend.uuid)[5:] self.config.save() except Exception as msg: self.warning(f'Can\'t enable plugin, {plugin}: {msg}!') self.error(traceback.format_exc()) def setup_part2(self): '''Initializes the basic and optional services/devices and the enabled plugins (backends).''' self.setup_ssdp_server() if not self.ssdp_server: raise Exception('Unable to initialize an ssdp server') self.msearch = MSearch(self.ssdp_server, test=self.is_unittest) reactor.addSystemEventTrigger( 'before', 'shutdown', self.shutdown, force=True, ) self.setup_web_server() if not self.urlbase: raise Exception('Unable to initialize an web server') self.setup_plugins() # Control Point Initialization if (self.config.get('controlpoint', 'no') == 'yes' or self.config.get('json', 'no') == 'yes'): self.ctrl = ControlPoint(self) # Json Interface Initialization if self.config.get('json', 'no') == 'yes': from coherence.json_service import JsonInterface self.json = JsonInterface(self.ctrl) # Transcoder Initialization if self.config.get('transcoding', 'no') == 'yes': from coherence.transcoder import TranscoderManager self.transcoder_manager = TranscoderManager(self) # DBus Initialization if self.config.get('use_dbus', 'no') == 'yes': try: from coherence import dbus_service if self.ctrl is None: self.ctrl = ControlPoint(self) self.ctrl.auto_client_append('InternetGatewayDevice') self.dbus = dbus_service.DBusPontoon(self.ctrl) except Exception as msg: self.warning(f'Unable to activate dbus sub-system: {msg}') self.debug(traceback.format_exc()) def add_plugin(self, plugin, **kwargs): self.info(f'adding plugin {plugin}') self.available_plugins = Plugins() # TODO clean up this exception concept try: plugin_class = self.available_plugins.get(plugin, None) if plugin_class is None: raise KeyError for device in plugin_class.implements: try: device_class = globals().get(device, None) if device_class is None: raise KeyError self.info(f'Activating {plugin} plugin as {device}...') new_backend = device_class(self, plugin_class, **kwargs) self.active_backends[str(new_backend.uuid)] = new_backend return new_backend except KeyError: self.warning(f'Can\'t enable {plugin} plugin, ' f'sub-system {device} not found!') except Exception as e1: self.exception(f'Can\'t enable {plugin} plugin for ' f'sub-system {device} [exception: {e1}]') self.debug(traceback.format_exc()) except KeyError: self.warning(f'Can\'t enable {plugin} plugin, not found!') except Exception as e2: self.warning(f'Can\'t enable {plugin} plugin, {e2}!') self.debug(traceback.format_exc()) def remove_plugin(self, plugin): '''Removes a backend from Coherence Args: plugin (object): is the object return by add_plugin or an UUID string. ''' if isinstance(plugin, str): try: plugin = self.active_backends[plugin] except KeyError: self.warning(f'no backend with the uuid {plugin} found') return '' try: del self.active_backends[str(plugin.uuid)] self.info(f'removing plugin {plugin}') plugin.unregister() return plugin.uuid except KeyError: self.warning(f'no backend with the uuid {plugin.uuid} found') return '' @staticmethod def writeable_config(): '''Do we have a new-style config file''' return False def store_plugin_config(self, uuid, items): '''Find the backend with uuid and store in its the config the key and value pair(s).''' plugins = self.config.get('plugin') if plugins is None: self.warning('storing a plugin config option is only possible' ' with the new config file format') return if isinstance(plugins, dict): plugins = [plugins] uuid = str(uuid) if uuid.startswith('uuid:'): uuid = uuid[5:] for plugin in plugins: try: if plugin['uuid'] == uuid: for k, v in list(items.items()): plugin[k] = v self.config.save() except Exception as e: self.warning(f'Coherence.store_plugin_config: {e}') else: self.info(f'storing plugin config option ' f'for {uuid} failed, plugin not found') def receiver(self, signal, *args, **kwargs): pass def shutdown(self, force=False): if self.__incarnations > 1 and not force: self.__incarnations -= 1 return if self.dbus: self.dbus.shutdown() self.dbus = None for backend in self.active_backends.values(): backend.unregister() self.active_backends = {} # send service unsubscribe messages if self.web_server is not None: if hasattr(self.web_server, 'endpoint_listen'): if self.web_server.endpoint_listen is not None: self.web_server.endpoint_listen.cancel() self.web_server.endpoint_listen = None if self.web_server.endpoint_port is not None: self.web_server.endpoint_port.stopListening() if hasattr(self.web_server, 'ws_endpoint_listen'): if self.web_server.ws_endpoint_listen is not None: self.web_server.ws_endpoint_listen.cancel() self.web_server.ws_endpoint_listen = None if self.web_server.ws_endpoint_port is not None: self.web_server.ws_endpoint_port.stopListening() try: if hasattr(self.msearch, 'double_discover_loop'): self.msearch.double_discover_loop.stop() if hasattr(self.msearch, 'port'): self.msearch.port.stopListening() if hasattr(self.ssdp_server, 'resend_notify_loop'): self.ssdp_server.resend_notify_loop.stop() if hasattr(self.ssdp_server, 'port'): self.ssdp_server.port.stopListening() # self.renew_service_subscription_loop.stop() except Exception: pass dev_l = [] for root_device in self.get_devices(): if hasattr(root_device, 'root_device_detection_completed'): root_device.unbind( root_device_detection_completed=self.add_device) for device in root_device.get_devices(): dd = device.unsubscribe_service_subscriptions() dd.addCallback(device.remove) dev_l.append(dd) rd = root_device.unsubscribe_service_subscriptions() rd.addCallback(root_device.remove) dev_l.append(rd) def homecleanup(result): # cleans up anything left over self.ssdp_server.unbind(new_device=self.create_device) self.ssdp_server.unbind(removed_device=self.remove_device) self.ssdp_server.shutdown() if self.ctrl: self.ctrl.shutdown() self.warning('Coherence UPnP framework shutdown') return result dl = defer.DeferredList(dev_l) dl.addCallback(homecleanup) return dl def check_devices(self): '''Iterate over devices and their embedded ones and renew subscriptions.''' for root_device in self.get_devices(): root_device.renew_service_subscriptions() for device in root_device.get_devices(): device.renew_service_subscriptions() def subscribe(self, name, callback): self._callbacks.setdefault(name, []).append(callback) def unsubscribe(self, name, callback): callbacks = self._callbacks.get(name, []) if callback in callbacks: callbacks.remove(callback) self._callbacks[name] = callbacks def callback(self, name, *args): for callback in self._callbacks.get(name, []): callback(*args) def get_device_by_host(self, host): found = [] for device in self.devices: if device.get_host() == host: found.append(device) return found def get_device_with_usn(self, usn): found = None for device in self.devices: if device.get_usn() == usn: found = device break return found def get_device_with_id(self, device_id): # print(f'get_device_with_id [{type(device_id)}]: {device_id}') found = None for device in self.devices: id = device.get_id() if device_id[:5] != 'uuid:': id = id[5:] if id == device_id: found = device break return found def get_devices(self): # print(f'get_devices: {self.devices}') return self.devices def get_local_devices(self): # print(f'get_local_devices: ' # f'{[d for d in self.devices if d.manifestation == "local"]}') return [d for d in self.devices if d.manifestation == 'local'] def get_nonlocal_devices(self): # print(f'get_nonlocal_devices: ' # f'{[d for d in self.devices if d.manifestation == "remote"]}') return [d for d in self.devices if d.manifestation == 'remote'] def is_device_added(self, infos): ''' Check if the device exists in our list of created :attr:`devices`. Args: infos (dict): Information about the device Returns: True if the device exists in our list of :attr:`devices`, otherwise, returns False. .. versionadded:: 0.9.0 ''' for d in self.devices: if d.st == infos['ST'] and d.usn == infos['USN']: return True return False def create_device(self, device_type, infos): if self.is_device_added(infos): self.warning( f'No need to create the device, we already added device: ' f'{infos["ST"]} with usn {infos["USN"]}...!!') return self.info(f'creating {infos["ST"]} {infos["USN"]}') if infos['ST'] == 'upnp:rootdevice': self.info(f'creating upnp:rootdevice {infos["USN"]}') root = RootDevice(infos) root.bind(root_detection_completed=self.add_device) else: self.info(f'creating device/service {infos["USN"]}') root_id = infos['USN'][:-len(infos['ST']) - 2] root = self.get_device_with_id(root_id) # TODO: must check that this is working as expected device = Device(root, udn=infos['UDN']) def add_device(self, device, *args): self.info(f'adding device {device.get_id()} {device.get_usn()} ' + f'{device.friendly_device_type}') self.devices.append(device) self.dispatch_event('coherence_device_detection_completed', device=device) def remove_device(self, device_type, infos): self.info(f'removed device {infos["ST"]} {infos["USN"]}') device = self.get_device_with_usn(infos['USN']) if device: self.dispatch_event('coherence_device_removed', infos['USN'], device.client) self.devices.remove(device) device.remove() if infos['ST'] == 'upnp:rootdevice': self.dispatch_event( 'coherence_root_device_removed', infos['USN'], device.client, ) self.callback('removed_device', infos['ST'], infos['USN']) def add_web_resource(self, name, sub): self.children[name] = sub def remove_web_resource(self, name): try: del self.children[name] except KeyError: ''' probably the backend init failed ''' pass @staticmethod def check_louie(receiver, signal, method='connect'): ''' Check if the connect or disconnect method's arguments are valid in order to automatically convert to EventDispatcher's bind The old valid signals are: - Coherence.UPnP.Device.detection_completed - Coherence.UPnP.RootDevice.detection_completed - Coherence.UPnP.Device.removed - Coherence.UPnP.RootDevice.removed .. versionadded:: 0.9.0 ''' if not callable(receiver): raise Exception('The receiver should be callable in order to use' + ' the method {method}') if not signal: raise Exception( f'We need a signal in order to use method {method}') if not (signal.startswith('Coherence.UPnP.Device.') or signal.startswith('Coherence.UPnP.RootDevice.')): raise Exception( 'We need a signal an old signal starting with: ' + '"Coherence.UPnP.Device." or "Coherence.UPnP.RootDevice."') def connect(self, receiver, signal=None, sender=None, weak=True): ''' Wrapper method around the deprecated method louie.connect. It will check if the passed signal is supported by executing the method :meth:`check_louie`. .. warning:: This will probably be removed at some point, if you use the connect method you should migrate to the new event system EventDispatcher. .. versionchanged:: 0.9.0 Added EventDispatcher's compatibility for some basic signals ''' self.check_louie(receiver, signal, 'connect') if signal.endswith('.detection_completed'): self.bind(coherence_device_detection_completed=receiver) elif signal.endswith('.Device.removed'): self.bind(coherence_device_removed=receiver) elif signal.endswith('.RootDevice.removed'): self.bind(coherence_root_device_removed=receiver) else: raise Exception( f'Unknown signal {signal}, we cannot bind that signal.') def disconnect(self, receiver, signal=None, sender=None, weak=True): ''' Wrapper method around the deprecated method louie.disconnect. It will check if the passed signal is supported by executing the method :meth:`check_louie`. .. warning:: This will probably be removed at some point, if you use the disconnect method you should migrate to the new event system EventDispatcher. .. versionchanged:: 0.9.0 Added EventDispatcher's compatibility for some basic signals ''' self.check_louie(receiver, signal, 'disconnect') if signal.endswith('.detected'): self.unbind(coherence_device_detection_completed=receiver) elif signal.endswith('.removed'): self.unbind(control_point_client_removed=receiver) else: raise Exception( f'Unknown signal {signal}, we cannot unbind that signal.')
def setup_part2(self): '''Initializes the basic and optional services/devices and the enabled plugins (backends).''' self.info(f'running on host: {self.hostname}') if self.hostname.startswith('127.'): self.warning(f'detection of own ip failed, using {self.hostname} ' f'as own address, functionality will be limited') unittest = self.config.get('unittest', 'no') unittest = False if unittest == 'no' else True try: # TODO: add ip/interface bind self.ssdp_server = SSDPServer(test=unittest) except CannotListenError as err: self.error(f'Error starting the SSDP-server: {err}') self.debug('Error starting the SSDP-server', exc_info=True) reactor.stop() return # maybe some devices are already notified, so we enforce # to create the device, if it is not already added...and # then we connect the signals for new detections. for st, usn in self.ssdp_server.root_devices: self.create_device(st, usn) self.ssdp_server.bind(new_device=self.create_device) self.ssdp_server.bind(removed_device=self.remove_device) self.ssdp_server.subscribe('new_device', self.add_device) self.ssdp_server.subscribe('removed_device', self.remove_device) self.msearch = MSearch(self.ssdp_server, test=unittest) reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, force=True) # Web Server Initialization try: # TODO: add ip/interface bind if self.config.get('web-ui', 'no') != 'yes': self.web_server = WebServer(None, self.web_server_port, self) else: self.web_server = WebServerUi(self.web_server_port, self, unittests=unittest) except CannotListenError: self.error( f'port {self.web_server_port} already in use, aborting!') reactor.stop() return self.urlbase = f'http://{self.hostname}:{self.web_server_port:d}/' # self.renew_service_subscription_loop = \ # task.LoopingCall(self.check_devices) # self.renew_service_subscription_loop.start(20.0, now=False) # Plugins Initialization try: plugins = self.config['plugin'] if isinstance(plugins, dict): plugins = [plugins] except Exception: plugins = None if plugins is None: plugins = self.config.get('plugins', None) if plugins is None: self.info('No plugin defined!') else: if isinstance(plugins, dict): for plugin, arguments in list(plugins.items()): try: if not isinstance(arguments, dict): arguments = {} self.add_plugin(plugin, **arguments) except Exception as msg: self.warning(f'Can\'t enable plugin, {plugin}: {msg}!') self.info(traceback.format_exc()) else: for plugin in plugins: try: if plugin['active'] == 'no': continue except (KeyError, TypeError): pass try: backend = plugin['backend'] arguments = copy.copy(plugin) del arguments['backend'] backend = self.add_plugin(backend, **arguments) if self.writeable_config(): if 'uuid' not in plugin: plugin['uuid'] = str(backend.uuid)[5:] self.config.save() except Exception as msg: self.warning(f'Can\'t enable plugin, {plugin}: {msg}!') self.info(traceback.format_exc()) self.external_address = ':'.join( (self.hostname, str(self.web_server_port))) # Control Point Initialization if self.config.get('controlpoint', 'no') == 'yes' or self.config.get( 'json', 'no') == 'yes': self.ctrl = ControlPoint(self) # Json Interface Initialization if self.config.get('json', 'no') == 'yes': from coherence.json_service import JsonInterface self.json = JsonInterface(self.ctrl) # Transcoder Initialization if self.config.get('transcoding', 'no') == 'yes': from coherence.transcoder import TranscoderManager self.transcoder_manager = TranscoderManager(self) # DBus Initialization if self.config.get('use_dbus', 'no') == 'yes': try: from coherence import dbus_service if self.ctrl is None: self.ctrl = ControlPoint(self) self.ctrl.auto_client_append('InternetGatewayDevice') self.dbus = dbus_service.DBusPontoon(self.ctrl) except Exception as msg: self.warning(f'Unable to activate dbus sub-system: {msg}') self.debug(traceback.format_exc())