Beispiel #1
0
    def create_root_device_instance(self, manufacturer: str, modelNumber: str,
                                    modelDescription: str) -> UpnpRootDevice:
        """
            Method that is called to create an instance of an extension for a specific UPnP root device.  If a device
            extension is not found matching the specified parameters then a generic UpnpRootDevice instance is
            created and returned.

            :param manufacturer: The manufacturer associated with the root device.
            :param modelNumber: The model number associated with the root device.
            :param modelDescription: The model description associated with the root device.

            :returns: An instance of an root device extension of a specific manaufacturer or a default generic
                      UpnpRootDevice.
        """
        deviceClass = UpnpRootDevice

        if manufacturer is not None and modelNumber is not None and modelDescription is not None:
            extkey = generate_extension_key(manufacturer, modelNumber,
                                            modelDescription)
            if extkey in self._dyn_root_device_registry:
                deviceClass = self._dyn_root_device_registry[extkey]
            elif extkey in self._std_root_device_registry:
                deviceClass = self._std_root_device_registry[extkey]

        dev_inst = deviceClass(manufacturer, modelNumber, modelDescription)
        return dev_inst
Beispiel #2
0
    def _locked_populate_services_descriptions(self, factory: "UpnpFactory",
                                               description: UpnpDevice1Device):
        """
            Takes in a device description and processes it for service descriptions.

            :param factory: The UpnpFactory that has registered exentsion types that is used to instantiate
                            services when the UPNP device description is processed.
            :param description: The UpnpDevice1Device object that contains information about the device and its services.
        """
        # pylint: disable=protected-access
        for serviceInfo in description.serviceList:
            serviceManufacturer = normalize_name_for_path(
                serviceInfo.serviceManufacturer)
            serviceType = serviceInfo.serviceType

            svckey = generate_extension_key(serviceManufacturer, serviceType)
            if serviceType not in self._services_descriptions:
                self._services_descriptions[svckey] = serviceInfo

                svc_inst = factory.create_service_instance(
                    serviceManufacturer, serviceType)
                if svc_inst is not None:
                    device_ref = weakref.ref(self)
                    svc_inst._proxy_link_service_to_device(
                        device_ref, serviceInfo)
                    self._services[svckey] = svc_inst
            else:
                svc_inst = self._services[svckey]

        return
Beispiel #3
0
 def _scan_for_service_extensions_under_folder(self, directory,
                                               service_table):
     """
         Method that scans a code container and its descendants for UpnpServiceProxy objects.
     """
     extcoll = collect_extensions_under_folder(directory, UpnpServiceProxy)
     for _, extcls in extcoll:
         if (hasattr(extcls, "SERVICE_MANUFACTURER")
                 and hasattr(extcls, "SERVICE_TYPE")):
             svc_manufacturer = getattr(extcls, "SERVICE_MANUFACTURER")
             svc_type = getattr(extcls, "SERVICE_TYPE")
             extkey = generate_extension_key(svc_manufacturer, svc_type)
             self._register_service(extkey, extcls, service_table)
     return
Beispiel #4
0
 def _scan_for_device_extensions_under_folder(self, directory,
                                              device_table):
     """
         Method that scans a code container and its descendants for UpnpRootDevice objects.
     """
     extcoll = collect_extensions_under_folder(directory, UpnpRootDevice)
     for _, extcls in extcoll:
         if hasattr(extcls, "MANUFACTURER") and hasattr(
                 extcls, "MODEL_NUMBER") and hasattr(
                     extcls, "MODEL_DESCRIPTION"):
             extkey = generate_extension_key(
                 getattr(extcls, "MANUFACTURER"),
                 getattr(extcls, "MODEL_NUMBER"),
                 getattr(extcls, "MODEL_DESCRIPTION"))
             self._register_root_device(extkey, extcls, device_table)
     return
Beispiel #5
0
    def lookup_service(
        self,
        serviceManufacturer: str,
        serviceType: str,
        allow_none: bool = False,
        factory: Optional["UpnpFactory"] = None
    ) -> Union[UpnpServiceProxy, None]:
        """
            Looks up a service proxy based on the service manufacturer and service type specified.

            :param serviceManufacturer: The manufacturer associated with the device and service manufacturer.
            :param serviceType: The service type of the service to lookup.
            :param allow_none: If True, this API will not thrown an exception if the service cannot be found.

            :returns: The service proxy associated with the manufacturer and service type provided or None.
        """
        svc = None

        serviceManufacturer = normalize_name_for_path(serviceManufacturer)
        svckey = generate_extension_key(serviceManufacturer, serviceType)

        self._device_lock.acquire()
        try:
            if svckey in self._services:
                svc = self._services[svckey]
            elif factory is not None:
                self._device_lock.release()
                try:
                    svc = factory.create_service_instance(
                        serviceManufacturer, serviceType)
                finally:
                    self._device_lock.acquire()

                if svc is not None:
                    self._services[svckey] = svc
        finally:
            self._device_lock.release()

        if svc is None and not allow_none:
            errmsg = "The service mfg={} serviceType={} was not found or is not available.".format(
                serviceManufacturer, serviceType)
            raise AKitServiceUnAvailableError(errmsg)

        return svc
Beispiel #6
0
    def create_service_instance(self, serviceManufacturer,
                                serviceType) -> Union[UpnpServiceProxy, None]:
        """
            Method that is called to create an instance of an extension for a specific UPnP service proxy.  If a
            service proxy extension is not found matching the specified parameters then None is returned.

            :param manufacturer: The manufacturer associated with the root device.
            :param modelNumber: The model number associated with the root device.
            :param modelDescription: The model description associated with the root device.

            :returns: An instance of an root device extension of a specific manaufacturer or a default generic
                      UpnpRootDevice.
        """
        serviceInst = None
        if serviceType is not None:
            extkey = generate_extension_key(serviceManufacturer, serviceType)
            if extkey in self._dyn_service_registry:
                serviceClass = self._dyn_service_registry[extkey]
                serviceInst = serviceClass()
            elif extkey in self._std_service_registry:
                serviceClass = self._std_service_registry[extkey]
                serviceInst = serviceClass()
        return serviceInst
Beispiel #7
0
    def subscribe_to_events(self,
                            service: UpnpServiceProxy,
                            renew: bool = False,
                            timeout: Optional[float] = None):
        """
            Creates a subscription to the event name specified and returns a
            UpnpEventVar object that can be used to read the current value for
            the given event.

            :param service: A :class:`akit.interop.upnp.services.upnpserviceproxy.UpnpServiceProxy`
                            for which to subscribe to events.
            :param timeout: The timeout to pass as a header when creating the subscription.
        """
        sub_sid = None
        sub_expires = None

        if len(service.SERVICE_EVENT_VARIABLES) > 0:

            service_type = service.SERVICE_TYPE

            new_subscription = False
            self._device_lock.acquire()
            try:
                if not service_type in self._subscriptions:
                    new_subscription = True
                    self._subscriptions[service_type] = (None, None)
                else:
                    sub_sid, sub_expires = self._subscriptions[service_type]

                    if sub_sid is None and sub_expires is None:
                        # The service was unsubscribed so this is the same as a new
                        # subscription.
                        new_subscription = True
            finally:
                self._device_lock.release()

            check_time = datetime.now() + timedelta(
                seconds=TIMEDELTA_RENEWAL_WINDOW)
            if sub_expires is not None and check_time > sub_expires:
                renew = True

            if new_subscription or renew:
                # If we created an uninitialized variable and added it to the subsciptions table
                # we need to statup the subsciption here.  If the startup process fails, we can
                # later remove the subscription from the subscription table.

                serviceManufacturer = normalize_name_for_path(
                    self.MANUFACTURER)
                svckey = generate_extension_key(serviceManufacturer,
                                                service_type)
                service = self._services[svckey]

                subscribe_url = urljoin(self.URLBase, service.eventSubURL)
                subscribe_auth = ""

                coord = self._coord_ref()

                ifname = self._primary_route[MSearchRouteKeys.IFNAME]

                callback_url = coord.lookup_callback_url_for_interface(ifname)
                if callback_url is None:
                    errmsg = "No callback url found for ifname={}".format(
                        ifname)
                    raise AKitRuntimeError(errmsg) from None
                callback_url = "<http://%s>" % callback_url

                headers = {
                    "HOST": self._host,
                    "User-Agent": AKitHttpHeaders.USER_AGENT,
                    "CALLBACK": callback_url,
                    "NT": "upnp:event"
                }
                if timeout is not None:
                    headers["TIMEOUT"] = "Seconds-%s" % timeout
                resp = requests.request("SUBSCRIBE",
                                        subscribe_url,
                                        headers=headers,
                                        auth=subscribe_auth)

                if resp.status_code == 200:
                    #============================== Expected Response Headers ==============================
                    # SID: uuid:RINCON_7828CA09247C01400_sub0000000207
                    # TIMEOUT: Second-86400
                    # Server: Linux UPnP/1.0 Sonos/62.1-82260-monaco_dev (ZPS13)
                    # Connection: close
                    resp_headers = {
                        k.upper(): v
                        for k, v in resp.headers.items()
                    }

                    nxtheader = None
                    try:
                        nxtheader = "SID"
                        sub_sid = resp_headers[nxtheader]

                        nxtheader = "TIMEOUT"
                        sub_timeout_str = resp_headers[nxtheader]
                    except KeyError as kerr:
                        errmsg = "Event subscription response was missing in %r header." % nxtheader
                        raise AKitCommunicationsProtocolError(errmsg) from kerr

                    sub_timeout = None
                    mobj = REGEX_SUBSCRIPTION_TIMEOUT.match(sub_timeout_str)
                    if mobj is not None:
                        timeout_str = mobj.groups()[0]
                        sub_timeout = 86400 if timeout_str == "infinite" else int(
                            timeout_str)

                    if sub_sid is not None:
                        sub_expires = datetime.now() + timedelta(
                            seconds=sub_timeout)
                        self._device_lock.acquire()
                        try:
                            self._sid_to_service_lookup[sub_sid] = service
                            self._subscriptions[service_type] = (sub_sid,
                                                                 sub_expires)
                        finally:
                            self._device_lock.release()

                        # Notify the coordinator which device has this subscription, we need to
                        # register the subscription ID so the coordinator can forward along
                        # notify message content
                        coord.register_subscription_for_device(sub_sid, self)
                    else:
                        self._device_lock.acquire()
                        try:
                            if service_type in self._subscriptions:
                                del self._subscriptions[service_type]
                        finally:
                            self._device_lock.release()

                else:
                    context_msg = "SUBSCRIBING at {}:".format(subscribe_url)
                    details = {
                        "SERVICE_KEY:": svckey,
                        "URL_BASE": self.URLBase,
                        "EVENT_SUB_URL": service.eventSubURL
                    }
                    raise_for_http_status(context_msg, resp, details=details)

        return sub_sid, sub_expires