Beispiel #1
0
    def __init__(self,
                 ssdp,
                 start=True,
                 interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr=DEFAULT_SSDP_ADDR,
                 ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)
Beispiel #2
0
    def __init__(self, server_name, xml_description_filename, max_age=1800,
                receive_notify=True, http_version="1.1",
		search_type="sspd:all", additional_headers={}):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        self.receive_notify = receive_notify
        self.running = False
	self.http_version = http_version
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
	self.additional_headers = additional_headers
	self.search_type = search_type
        self.udp_transport = UDPTransport()
        self.udp_listener = UDPListener(SSDP_ADDR, SSDP_PORT,
                                        data_callback=self._datagram_received)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)
Beispiel #3
0
    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr='239.255.255.250', ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)
Beispiel #4
0
    def __init__(self, server_name, xml_description_filename, max_age=1800, receive_notify=True, udp_listener=""):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        log.debug("max_age: %s", max_age)
        self.receive_notify = receive_notify
        self.running = False
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
        self.udp_transport = UDPTransport()
        if udp_listener == "":
            self.udp_listener = UDPListener(SSDP_ADDR, SSDP_PORT, data_callback=self._datagram_received)
        else:
            self.udp_listener = None
            udp_listener.subscribe(self)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)
Beispiel #5
0
    def __init__(self,
                 server_name,
                 xml_description_filename,
                 max_age=1800,
                 receive_notify=True,
                 http_version="1.1",
                 search_type="sspd:all",
                 additional_headers={}):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        self.receive_notify = receive_notify
        self.running = False
        self.http_version = http_version
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
        self.additional_headers = additional_headers
        self.search_type = search_type
        self.udp_transport = UDPTransport()
        self.udp_listener = UDPListener(SSDP_ADDR,
                                        SSDP_PORT,
                                        data_callback=self._datagram_received)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)
Beispiel #6
0
    def __init__(self,
                 server_name,
                 xml_description_filename,
                 max_age=1800,
                 receive_notify=True,
                 udp_listener=''):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        log.debug("max_age: %s", max_age)
        self.receive_notify = receive_notify
        self.running = False
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
        self.udp_transport = UDPTransport()
        if udp_listener == '':
            self.udp_listener = UDPListener(
                SSDP_ADDR, SSDP_PORT, data_callback=self._datagram_received)
        else:
            self.udp_listener = None
            udp_listener.subscribe(self)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)
Beispiel #7
0
    def __init__(self, control_point, start=True):
        """ Constructor for the MulticastEventListener class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(UPnPDefaults.MULTICAST_EVENT_ADDR,
                                      UPnPDefaults.MULTICAST_EVENT_PORT,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.control_point = control_point
        if start:
            self.start()
Beispiel #8
0
    def __init__(self, control_point, start=True):
        """ Constructor for the MulticastEventListener class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(UPnPDefaults.MULTICAST_EVENT_ADDR,
                                      UPnPDefaults.MULTICAST_EVENT_PORT,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.control_point = control_point
        if start:
            self.start()
Beispiel #9
0
    def __init__(self, parent_udn, service, event_reload_time, force_event_reload):
        self.service = service
        self.parent_udn = parent_udn
        self.udp_transport = UDPTransport()
        


        self.listen_udp = UDPListener(UPnPDefaults.MULTICAST_EVENT_ADDR,
                                      UPnPDefaults.MULTICAST_EVENT_PORT,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        
        
        
        self.eventing_variables = {}
        self.event_key = 0
        self.event_reload_time = event_reload_time
        self.force_event_reload = force_event_reload

        if not self.force_event_reload:
            self.l_call = LoopingCall(self.send_variables)
            reactor.add_after_stop_func(self.stop)
        self._is_running = False
Beispiel #10
0
class SSDPServer(object):
    """ Implementation of a SSDP server.

    The notify_received and search_received methods are called when the
    appropriate type of datagram is received by the server.
    """

    msg_already_started = 'tried to start() SSDPServer when already started'
    msg_already_stopped = 'tried to stop() SSDPServer when already stopped'

    def __init__(self, server_name, xml_description_filename, max_age=1800,
                receive_notify=True, http_version="1.1",
		search_type="sspd:all", additional_headers={}):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        self.receive_notify = receive_notify
        self.running = False
	self.http_version = http_version
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
	self.additional_headers = additional_headers
	self.search_type = search_type
        self.udp_transport = UDPTransport()
        self.udp_listener = UDPListener(SSDP_ADDR, SSDP_PORT,
                                        data_callback=self._datagram_received)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)

    def is_running(self):
        """ Returns True if the SSDPServer is running, False otherwise.
        """
        return self.running

    def start(self):
        """ Starts the SSDPServer.
        """
        if not self.is_running():
            self.udp_listener.start()
            self.running = True
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Sends bye bye notifications and stops the SSDPServer.
        """
        if self.is_running():
            # Avoid racing conditions
            own_temp = self.advertised.copy()
            for usn in own_temp:
                self._do_byebye(usn)

            self.renew_loop.stop()
            self.udp_listener.stop()
            self.running = False
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys the SSDPServer.
        """
        if self.is_running():
            self.stop()
        self.renew_loop.destroy()
        self.udp_listener.destroy()
        self._cleanup()

    def clear_device_list(self):
        """ Clears the device list.
        """
        self.known_device.clear()

    def discovered_device_failed(self, dev):
        """ Device could not be fully built, so forget it.
        """
        usn = dev['USN']
        if usn in self.known_device:
            self.known_device.pop(usn)

    def is_known_device(self, usn):
        """ Returns if the device with the passed usn is already known.

        @param usn: device's usn
        @type usn: string

        @return: True if it is known
        @rtype: boolean
        """
        return usn in self.known_device

    def subscribe(self, name, callback):
        """ Subscribes a callback for an event.

        @param name: name of the event. May be "new_device_event" or
                     "removed_device_event"
        @param callback: callback

        @type name: string
        @type callback: callable
        """
        self._callbacks.setdefault(name, []).append(callback)

    def unsubscribe(self, name, callback):
        """ Unsubscribes a callback for an event.

        @param name: name of the event
        @param callback: callback

        @type name: string
        @type callback: callable
        """
        callbacks = self._callbacks.get(name, [])
        [callbacks.remove(c) for c in callbacks]
        self._callbacks[name] = callbacks

    def announce_device(self):
        """ Announces the device.
        """
        [self._do_notify(usn) for usn in self.advertised]

    def register_device(self, device):
        """ Registers a device on the SSDP server.

        @param device: device to be registered
        @type device: Device
        """
        self._register_device(device)
        if device.is_root_device():
            [self._register_device(d) for d in device.devices.values()]

    # Messaging

    def _datagram_received(self, data, (host, port)):
        """ Handles a received multicast datagram.

        @param data: raw data
        @param host: datagram source host
        @param port: datagram source port

        @type data: string
        @type host: string
        @type port: integer
        """
        try:
            header, payload = data.split('\r\n\r\n')
        except ValueError, err:
            log.error('Error while receiving datagram packet: %s', str(err))
            return
Beispiel #11
0
class SSDPServer(object):
    """ Implementation of a SSDP server.

    The notify_received and search_received methods are called when the
    appropriate type of datagram is received by the server.
    """

    msg_already_started = 'tried to start() SSDPServer when already started'
    msg_already_stopped = 'tried to stop() SSDPServer when already stopped'

    def __init__(self,
                 server_name,
                 xml_description_filename,
                 max_age=1800,
                 receive_notify=True,
                 udp_listener=''):
        """ Constructor for the SSDPServer class.

        @param server_name: server name
        @param xml_description_filename: XML description filename
        @param max_age: max age parameter, default 1800.
        @param receive_notify: if False, ignores notify messages

        @type server_name: string
        @type xml_description_filename:
        @type max_age: integer
        @type receive_notify: boolean
        """
        self.server_name = server_name
        self.xml_description_filename = xml_description_filename
        self.max_age = max_age
        log.debug("max_age: %s", max_age)
        self.receive_notify = receive_notify
        self.running = False
        self.known_device = {}
        self.advertised = {}
        self._callbacks = {}
        self.udp_transport = UDPTransport()
        if udp_listener == '':
            self.udp_listener = UDPListener(
                SSDP_ADDR, SSDP_PORT, data_callback=self._datagram_received)
        else:
            self.udp_listener = None
            udp_listener.subscribe(self)
        self.renew_loop = LoopingCall(self._renew_notifications)
        self.renew_loop.start(0.8 * self.max_age, now=True)

    def is_running(self):
        """ Returns True if the SSDPServer is running, False otherwise.
        """
        return self.running

    def start(self):
        """ Starts the SSDPServer.
        """
        if not self.is_running():
            if self.udp_listener != None:
                self.udp_listener.start()
            self.running = True
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Sends bye bye notifications and stops the SSDPServer.
        """
        if self.is_running():
            # Avoid racing conditions
            own_temp = self.advertised.copy()
            for usn in own_temp:
                self._do_byebye(usn)

            self.renew_loop.stop()
            if self.udp_listener != None:
                self.udp_listener.stop()
            self.running = False
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys the SSDPServer.
        """
        if self.is_running():
            self.stop()
        self.renew_loop.destroy()
        if self.udp_listener != None:
            self.udp_listener.destroy()
        self._cleanup()

    def clear_device_list(self):
        """ Clears the device list.
        """
        self.known_device.clear()

    def discovered_device_failed(self, dev):
        """ Device could not be fully built, so forget it.
        """
        usn = dev['USN']
        if usn in self.known_device:
            self.known_device.pop(usn)

    def is_known_device(self, usn):
        """ Returns if the device with the passed usn is already known.

        @param usn: device's usn
        @type usn: string

        @return: True if it is known
        @rtype: boolean
        """
        return usn in self.known_device

    def subscribe(self, name, callback):
        """ Subscribes a callback for an event.

        @param name: name of the event. May be "new_device_event" or
                     "removed_device_event"
        @param callback: callback

        @type name: string
        @type callback: callable
        """
        self._callbacks.setdefault(name, []).append(callback)

    def unsubscribe(self, name, callback):
        """ Unsubscribes a callback for an event.

        @param name: name of the event
        @param callback: callback

        @type name: string
        @type callback: callable
        """
        callbacks = self._callbacks.get(name, [])
        [callbacks.remove(c) for c in callbacks]
        self._callbacks[name] = callbacks

    def announce_device(self):
        """ Announces the device.
        """
        log.debug("announce_device")
        [self._do_notify(usn) for usn in self.advertised]

    def register_device(self, device):
        """ Registers a device on the SSDP server.

        @param device: device to be registered
        @type device: Device
        """
        self._register_device(device)
        if device.is_root_device():
            [self._register_device(d) for d in device.devices.values()]

    # Messaging

    def _datagram_received(self, data, (host, port)):
        """ Handles a received multicast datagram.

        @param data: raw data
        @param host: datagram source host
        @param port: datagram source port

        @type data: string
        @type host: string
        @type port: integer
        """
        log.debug("SSDP._datagram_received host: %s, port: %s\ndata: %s", host,
                  port, data)
        try:
            header, payload = data.split('\r\n\r\n')
        except ValueError, err:
            log.error('Error while receiving datagram packet: %s', str(err))
            return
Beispiel #12
0
class MulticastEventListener:
    """ Represents a multicast event listener. Contains some control 
    functions for starting and stopping the listener.
    """

    msg_already_started = 'tried to start() MulticastEventListener when already started'
    msg_already_stopped = 'tried to stop() MulticastEventListener when already stopped'

    def __init__(self, control_point, start=True):
        """ Constructor for the MulticastEventListener class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(UPnPDefaults.MULTICAST_EVENT_ADDR,
                                      UPnPDefaults.MULTICAST_EVENT_PORT,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.control_point = control_point
        if start:
            self.start()

    def _datagram_received(self, data, address):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
        (host, port) = address
        try:
            cmd, headers = parse_http_response(data)
            body = data[data.find("<"):data.rfind(">")+1]
        except Exception as err:
            log.error('Error while receiving datagram packet: %s', str(err))
            return

        # Render notify message
        if not (cmd[0] == 'NOTIFY' and cmd[1] == '*' and cmd[2] == 'HTTP/1.0' and \
           'content-type' in headers and \
           headers['content-type'] == 'text/xml; charset="utf-8"' and \
           'nt' in headers and headers['nt'] == 'upnp:event' and \
           'nts' in headers and headers['nts'] == 'upnp:propchange' and \
           'host' in headers and 'usn' in headers and \
           'svcid' in headers and 'seq' in headers and \
           'lvl' in headers and 'bootid.upnp.org' in headers and \
           'content-length' in headers):

            log.warning('Invalid message')
            return

        addr = headers['host'].split(':')[0]
        port = int(headers['host'].split(':')[1])
        udn = headers['usn'].split('::')[0]
        service_type = headers['usn'].split('::')[1]
        svcid = headers['svcid']
        seq = int(headers['seq'])
        lvl = headers['lvl']
        content_length = int(headers['content-length'])
        bootid = int(headers['bootid.upnp.org'])

        if addr != UPnPDefaults.MULTICAST_EVENT_ADDR or \
           port != UPnPDefaults.MULTICAST_EVENT_PORT:
            log.warning('Invalid address %s:%d' % (addr, port))
            return

        changed_vars = read_notify_message_body(body)

        self.control_point._on_event('', changed_vars)

        for id, dev in list(self.control_point._known_devices.items()):
            service = self._find_service(dev, udn, service_type, svcid)
            if service != None:
                service._on_event(changed_vars)
                log.debug('Multicast event. Event changed vars: %s', changed_vars)

    def is_running(self):
        """ Returns True if the listener is running.

        @rtype: boolean
        """
        return self.listen_udp.is_running()

    def start(self):
        """ Starts the listener.
        """
        if not self.is_running():
            self.listen_udp.start()
            log.debug('Multicast event listener started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('Multicast event listener stopped')
            self.listen_udp.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self._cleanup()

    def _find_service(self, device, udn, service_type, svcid):
        """ Method to find a service.

        @param device: instance of a device
        @param udn: device id
        @param service_type: service type
        @param svcid: service id

        @type device: RootDevice or Device
        @type udn: string
        @type service_type: string
        @type svcid: string

        @return: if found, the service
        @rtype: Service or None
        """
        if device.udn != udn:
            for child_dev in list(device.devices.values()):
                service = self._find_service(child_dev, udn, service_type, svcid)
                if service:
                    return service
        else:
            for k, service in list(device.services.items()):
                if service.service_type == service_type and \
                   str(service.id) == svcid:
                    return service
            return None

    def _cleanup(self):
        """ Clean up references.
        """
        self.ssdp = None
        self.listen_udp = None
        self.control_point = None
Beispiel #13
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr='239.255.255.250', ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self, interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"

        @type interval: float
        @type search_type: string
        """
        if not self.is_running():
            self.listen_udp.start()
            self.loopcall._args = (search_type, )
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self, search_type=DEFAULT_SEARCH_TYPE):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
        log.info("Doing double discover for %s", search_type)
        self.discover(search_type)
        self.discover(search_type)

    def discover(self, type="ssdp:all"):
        """ Mounts and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
        req = ['M-SEARCH * HTTP/1.1',
                'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
                'MAN: "ssdp:discover"',
                'MX: 5',
                'ST: ' + type, '', '']
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
        cmd, headers = parse_http_response(data)
        if cmd[0] == 'HTTP/1.1' and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
                    self.ssdp._register(headers['usn'],
                                        headers['st'],
                                        headers['location'],
                                        headers['server'],
                                        headers['cache-control'])
Beispiel #14
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self,
                 ssdp,
                 start=True,
                 interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr=DEFAULT_SSDP_ADDR,
                 ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self,
              interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE,
              http_version="1.1",
              man='"ssdp:discover"',
              mx=1,
              additionals={}):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"
        @param http_version: http version for m-search (default is 1.1)
        @param man: man field for m-search (default is ssdp:discover)
        @param mx: mx field for m-search (default is 1)
        @param additionals: dict containing additional field to be appended
			    in the end of the m-search message (default is
			    a empty dictionary)

        @type interval: float
        @type search_type: string
        @type http_version: string
        @type man: string
        @type mx: int
        @type additionals: dict
        """
        if not self.is_running():
            self.ssdp.search_type = search_type
            self.listen_udp.start()
            self.loopcall._args = (
                search_type,
                http_version,
                man,
                mx,
                additionals,
            )
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self,
                        search_type=DEFAULT_SEARCH_TYPE,
                        http_version="1.1",
                        man='"ssdp:discover"',
                        mx=1,
                        additionals={}):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
        log.info("Doing double discover for %s, HTTP_VERSION=%s, MAN=%s, \
		 MX=%d, additionals=%s" % (search_type, http_version, man, mx, additionals))
        self.discover(search_type, http_version, man, mx, additionals)

    def discover(self,
                 search_type=DEFAULT_SEARCH_TYPE,
                 http_version="1.1",
                 man='"ssdp:discover"',
                 mx=1,
                 additionals={}):
        """ Builds and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
        if (mx > 120):
            mx = 120
        elif (mx < 1):
            mx = 1
        req = [
            'M-SEARCH * HTTP/%s' % http_version,
            'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
            'MAN: %s' % man,
            'MX: %s' % mx,
            'ST: %s' % search_type
        ]
        append = req.append
        [append('%s: %s' % (k, v)) for k, v in additionals.items()]
        append('')
        append('')
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
        cmd, headers = parse_http_response(data)
        if cmd[0].startswith('HTTP/1.') and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
                    default_fields_name = [
                        "usn", "st", "location", "server", "cache-control",
                        "ext"
                    ]
                    default_header = {}
                    for field in default_fields_name:
                        default_header[field] = headers.pop(field, "")
                    self.ssdp.register(default_header['usn'],
                                       default_header['st'],
                                       default_header['location'],
                                       default_header['server'],
                                       default_header['cache-control'],
                                       "remote", headers)
Beispiel #15
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr='239.255.255.250', ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.search_type = DEFAULT_SEARCH_TYPE
        self.udp_transport = UDPTransport()
#        self.listen_udp = UDPListener(ssdp_addr, ssdp_port,
        self.listen_udp = UDPListener(ssdp_addr, 2149,      # WMP is not picked up if 1900 is used for source
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self, interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"

        @type interval: float
        @type search_type: string
        """

#        interval = 30.0
        
        if not self.is_running():
            self.search_type = search_type
            self.listen_udp.start()
            
#            print ">>>>>>>>> interval: " + str(interval)
            
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self, search_type=DEFAULT_SEARCH_TYPE):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
#        print "<<<<<<<<< start double discover >>>>>>>>>"
        self.discover(search_type)
        self.discover(search_type)
#        print "<<<<<<<<< end double discover >>>>>>>>>"

    def discover(self, type="ssdp:all"):
#    def discover(self, type="upnp:rootdevice"):
        """ Mounts and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
        
#        type = "urn:schemas-upnp-org:device:MediaServer:1"
        type = "upnp:rootdevice"
        
#        req = ['M-SEARCH * HTTP/1.1',
#                'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
#                'MAN: "ssdp:discover"',
#                'MX: 5',
#                'ST: ' + type, '', '']
#        req = '\r\n'.join(req)
        req = ['M-SEARCH * HTTP/1.1',
                'HOST:%s:%d' % (self.ssdp_addr, self.ssdp_port),
                'MAN:"ssdp:discover"',
#                'Host:%s:%d' % (self.ssdp_addr, self.ssdp_port),
#                'Man:"ssdp:discover"',
                'MX:5',
                'ST:' + type, '', '', '']
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
#        print "datagram_received start"
        cmd, headers = parse_http_response(data)
        if cmd[0] == 'HTTP/1.1' and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
#                    print "_datagram_received _register"
#                    print "_datagram_received headers: " + str(headers)
                    self.ssdp._register(headers['usn'],
                                        headers['st'],
                                        headers['location'],
                                        headers['server'],
                                        headers['cache-control'])
Beispiel #16
0
class MSearch(object):
    """ Represents a MSearch. Contains some control functions for starting and
    stopping the search. While running, search will be repeated in regular
    intervals specified at construction or passed to the start() method.
    """

    msg_already_started = 'tried to start() MSearch when already started'
    msg_already_stopped = 'tried to stop() MSearch when already stopped'

    def __init__(self, ssdp, start=True, interval=DEFAULT_SEARCH_TIME,
                 ssdp_addr=DEFAULT_SSDP_ADDR, ssdp_port=1900):
        """ Constructor for the MSearch class.

        @param ssdp: ssdp server instance that will receive new device events
        and subscriptions
        @param start: if True starts the search when constructed
        @param interval: interval between searchs
        @param ssdp_addr: ssdp address for listening (UDP)
        @param ssdp_port: ssdp port for listening (UDP)

        @type ssdp: SSDPServer
        @type start: boolean
        @type interval: float
        @type ssdp_addr: string
        @type ssdp_port integer
        """
        self.ssdp = ssdp
        self.ssdp_addr = ssdp_addr
        self.ssdp_port = ssdp_port
        self.udp_transport = UDPTransport()
        self.listen_udp = UDPListener(ssdp_addr,
                                      data_callback=self._datagram_received,
                                      shared_socket=self.udp_transport.socket)
        self.loopcall = LoopingCall(self.double_discover)
        if start:
            self.start(interval)

    def is_running(self):
        """ Returns True if the search is running (it's being repeated in the
        interval given).

        @rtype: boolean
        """
        return self.loopcall.is_running()

    def start(self, interval=DEFAULT_SEARCH_TIME,
              search_type=DEFAULT_SEARCH_TYPE, http_version="1.1",
	      man='"ssdp:discover"', mx=1, additionals={}):
        """ Starts the search.

        @param interval: interval between searchs. Default is 600.0 seconds
        @param search_type: type of the search, default is "ssdp:all"
        @param http_version: http version for m-search (default is 1.1)
        @param man: man field for m-search (default is ssdp:discover)
        @param mx: mx field for m-search (default is 1)
        @param additionals: dict containing additional field to be appended
			    in the end of the m-search message (default is
			    a empty dictionary)

        @type interval: float
        @type search_type: string
        @type http_version: string
        @type man: string
        @type mx: int
        @type additionals: dict
        """
        if not self.is_running():
	    self.ssdp.search_type = search_type
            self.listen_udp.start()
            self.loopcall._args = (search_type, http_version, man, mx,
	    			   additionals, )
            self.loopcall.start(interval, now=True)
            log.debug('MSearch started')
        else:
            log.warning(self.msg_already_started)

    def stop(self):
        """ Stops the search.
        """
        if self.is_running():
            log.debug('MSearch stopped')
            self.listen_udp.stop()
            self.loopcall.stop()
        else:
            log.warning(self.msg_already_stopped)

    def destroy(self):
        """ Destroys and quits MSearch.
        """
        if self.is_running():
            self.stop()
        self.listen_udp.destroy()
        self.loopcall.destroy()
        self._cleanup()

    def double_discover(self, search_type=DEFAULT_SEARCH_TYPE,
    			http_version="1.1", man='"ssdp:discover"', mx=1,
			additionals={}):
        """ Sends a MSearch imediatelly. Each call to this method will yield a
        MSearch message, that is, it won't repeat automatically.
        """
        log.info("Doing double discover for %s, HTTP_VERSION=%s, MAN=%s, \
		 MX=%d, additionals=%s" % (search_type, http_version, man, mx,
		 			   additionals))
        self.discover(search_type, http_version, man, mx, additionals)

    def discover(self, search_type=DEFAULT_SEARCH_TYPE, http_version="1.1",
    		 man='"ssdp:discover"', mx=1, additionals={}):
        """ Builds and sends the discover message (MSearch).

        @param type: search type
        @type type: string
        """
	if (mx > 120):
	    mx = 120
	elif (mx < 1):
	    mx = 1
        req = ['M-SEARCH * HTTP/%s' % http_version,
               'HOST: %s:%d' % (self.ssdp_addr, self.ssdp_port),
               'MAN: %s' % man,
               'MX: %s' % mx,
               'ST: %s' % search_type]
	append = req.append
	[append('%s: %s' % (k, v)) for k, v in additionals.items()]
	append('')
	append('')
        req = '\r\n'.join(req)
        self.udp_transport.send_data(req, self.ssdp_addr, self.ssdp_port)

    def _datagram_received(self, data, (host, port)):
        """ Callback for the UDPListener when messages arrive.

        @param data: raw data received
        @param host: host where data came from
        @param port: port where data came from

        @type data: string
        @type host: string
        @type port: integer
        """
        cmd, headers = parse_http_response(data)
        if cmd[0].startswith('HTTP/1.') and cmd[1] == '200':
            if self.ssdp != None:
                if not self.ssdp.is_known_device(headers['usn']):
                    log.debug('Received MSearch answer %s,%s from %s:%s',
                              headers['usn'], headers['st'], host, port)
                    default_fields_name = ["usn", "st", "location", "server",
                                           "cache-control", "ext"]
                    default_header = {}
                    for field in default_fields_name:
                        default_header[field] = headers.pop(field, "")
                    self.ssdp.register(default_header['usn'],
                                       default_header['st'],
                                       default_header['location'],
                                       default_header['server'],
                                       default_header['cache-control'],
                                       "remote", headers)