コード例 #1
0
ファイル: event.py プロジェクト: aleixq/python3-brisa
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