class UPnPSubscription(BaseController): def __init__(self, callbacks, timeout, protocol): self._uuid = str(uuid4()) super(UPnPSubscription, self).__init__(channel="subs:" + self._uuid) self._callbacks = callbacks self._used_callback = 0 self._client = Client(self._callbacks[self._used_callback], self.channel).register(self) self._protocol = protocol self._seq = 0 if timeout > 0: self._expiry_timer = Timer \ (timeout, Event.create("upnp_subs_end"), self).register(self) @handler("registered") def _on_registered(self, component, parent): if component != self: return @handler("notification", channel=parent.notification_channel) def _on_notification_handler(self, state_vars): self._on_notification(state_vars) self.addHandler(_on_notification_handler) state_vars = dict() for name, method in getmembers \ (self.parent, lambda x: ismethod(x) and hasattr(x, "_evented_by")): state_vars[name] = method() if len(state_vars) > 0: self._on_notification(state_vars) self.fire(Log(logging.DEBUG, "Subscribtion for " + str(self._callbacks) + " on " + self.parent.notification_channel + " created"), "logger") def _on_notification(self, state_vars): root = Element(QName(UPNP_EVENT_NS, "propertyset")) for name, value in state_vars.items(): prop = SubElement(root, QName(UPNP_EVENT_NS, "property")) val = SubElement(prop, QName(UPNP_EVENT_NS, name)) if isinstance(value, bool): val.text = "1" if value else "0" else: val.text = str(value) misc.set_ns_prefixes(root, { "": UPNP_EVENT_NS }) writer = StringIO() writer.write("<?xml version='1.0' encoding='utf-8'?>") ElementTree(root).write(writer, encoding="utf-8") body = writer.getvalue() self.fire(Log(logging.DEBUG, "Notifying " + self._callbacks[self._used_callback] + " about " + str(state_vars)), "logger") self.fire(Request("NOTIFY", self._callbacks[self._used_callback], body, { "CONTENT-TYPE": "text/xml; charset=\"utf-8\"", "NT": "upnp:event", "NTS": "upnp:propchange", "SID": self.sid, "SEQ": self._seq })) self._seq += 1 @handler("upnp_subs_end") def _on_subs_end(self): self.unregister() self.fire(Log(logging.DEBUG, "Subscribtion for " + str(self._callbacks) + " on " + self.parent.notification_channel + " cancelled"), "logger") @handler("upnp_subs_renewal") def _on_renewal(self, timeout): self._expiry_timer.interval = timeout self._expiry_timer.reset() self.fire(Log(logging.DEBUG, "Subscribtion for " + str(self._callbacks) + " on " + self.parent.notification_channel + " renewed"), "logger") @property def sid(self): return "uuid:" + self._uuid @classmethod def sid2chan(cls, sid): return "subs:" + sid[5:]
class UPnPSubscription(BaseController): def __init__(self, callbacks, timeout, protocol): self._uuid = str(uuid4()) super(UPnPSubscription, self).__init__(channel="subs:" + self._uuid) self._callbacks = callbacks self._used_callback = 0 self._client = Client(self._callbacks[self._used_callback], self.channel).register(self) self._protocol = protocol self._seq = 0 if timeout > 0: self._expiry_timer = Timer \ (timeout, Event.create("upnp_subs_end"), self).register(self) @handler("registered") def _on_registered(self, component, parent): if component != self: return @handler("upnp_notification", channel=parent.notification_channel) def _on_notification_handler(self, state_vars): self._on_notification(state_vars) self.addHandler(_on_notification_handler) state_vars = dict() for name, method in getmembers \ (self.parent, lambda x: ismethod(x) and hasattr(x, "_evented_by")): state_vars[name] = method() if len(state_vars) > 0: self._on_notification(state_vars) self.fire(log(logging.DEBUG, "Subscribtion for " + str(self._callbacks) + " on " + self.parent.notification_channel + " created"), "logger") def _on_notification(self, state_vars): root = Element(QName(UPNP_EVENT_NS, "propertyset")) for name, value in state_vars.items(): prop = SubElement(root, QName(UPNP_EVENT_NS, "property")) val = SubElement(prop, QName(UPNP_EVENT_NS, name)) if isinstance(value, bool): val.text = "1" if value else "0" else: val.text = unicode(value) misc.set_ns_prefixes(root, { "": UPNP_EVENT_NS }) # Keep body as str for safe request handling body = "<?xml version='1.0' encoding='utf-8'?>" \ + ElementTree.tostring(root, encoding="utf-8") self.fire(log(logging.DEBUG, "Notifying " + self._callbacks[self._used_callback] + " about " + str(state_vars)), "logger") self.fire(request("NOTIFY", self._callbacks[self._used_callback], body, { "CONTENT-TYPE": "text/xml; charset=\"utf-8\"", "NT": "upnp:event", "NTS": "upnp:propchange", "SID": self.sid, "SEQ": self._seq })) self._seq += 1 @handler("upnp_subs_end") def _on_subs_end(self): self.unregister() self.fire(log(logging.DEBUG, "Subscribtion for " + str(self._callbacks) + " on " + self.parent.notification_channel + " cancelled"), "logger") @handler("upnp_subs_renewal") def _on_renewal(self, timeout): self._expiry_timer.interval = timeout self._expiry_timer.reset() self.fire(log(logging.DEBUG, "Subscribtion for " + str(self._callbacks) + " on " + self.parent.notification_channel + " renewed"), "logger") @property def sid(self): return "uuid:" + self._uuid @classmethod def sid2chan(cls, sid): return "subs:" + sid[5:]
class UPnPRootDevice(BaseComponent): def __init__(self, location, max_age, usn): super(UPnPRootDevice, self).__init__(channel=usn) self._location = location self._usn = usn self._ready = False self._comm_chan = "client." + usn self._client = Client(location, channel=self._comm_chan).register(self) @handler("response", channel=self._comm_chan) def _on_response(self, response): if response.status == http_client.OK: self._initialize(response.read()) self.addHandler(_on_response) @handler("error", channel=self._comm_chan) def _on_error(self, *args, **kwargs): self._client.close() self.unregister() self.addHandler(_on_error) self.fire(Request("GET", self._location), self._client) self._expiry_timer \ = Timer(max_age, UPnPDeviceByeBye(usn)).register(self) def _initialize(self, xml_src): data = XML(xml_src) self._friendly_name = data.findtext \ ("{%s}device/{%s}friendlyName" \ % (SSDP_DEVICE_SCHEMA, SSDP_DEVICE_SCHEMA)) icons = data.findall \ ("{%s}device/{%s}iconList/{%s}icon" \ % (SSDP_DEVICE_SCHEMA, SSDP_DEVICE_SCHEMA, SSDP_DEVICE_SCHEMA)) self._icons = [] for icon in icons: width = int(icon.findtext("{%s}width" % SSDP_DEVICE_SCHEMA)) height = int(icon.findtext("{%s}height" % SSDP_DEVICE_SCHEMA)) url = urljoin(self._location, icon.findtext("{%s}url" % SSDP_DEVICE_SCHEMA)) self._icons.append(IconInfo(width, height, url)) self._ready = True @handler("upnp_device_alive") def _on_device_alive \ (self, location, notification_type, max_age, server, usn): self._expiry_timer.interval = max_age self._expiry_timer.reset() @handler("upnp_device_bye_bye") def _on_device_bye_bye (self, usn): self._client.close() self.unregister() @property def usn(self): return getattr(self, "_usn", None) @property def location(self): return getattr(self, "_location", None) @property def ready(self): return getattr(self, "_ready", None) @property def friendly_name(self): return getattr(self, "_friendly_name", None) @property def icons(self): return copy(getattr(self, "_icons", None)) @property def valid_until(self): return self._expiry_timer._eTime \ if hasattr(self, "_expiry_timer") else None
class UPnPRootDevice(BaseComponent): def __init__(self, location, max_age, usn): super(UPnPRootDevice, self).__init__(channel=usn) self._location = location self._usn = usn self._ready = False self._comm_chan = "client." + usn self._client = Client(location, channel=self._comm_chan).register(self) @handler("response", channel=self._comm_chan) def _on_response(self, response): if response.status == httplib.OK: self._initialize(response.read()) self.addHandler(_on_response) @handler("error", channel=self._comm_chan) def _on_error(self, *args, **kwargs): self._client.close() self.unregister() self.addHandler(_on_error) self.fire(Request("GET", self._location), self._client) self._expiry_timer \ = Timer(max_age, UPnPDeviceByeBye(usn)).register(self) def _initialize(self, xml_src): data = XML(xml_src) self._friendly_name = data.findtext \ ("{%s}device/{%s}friendlyName" \ % (SSDP_DEVICE_SCHEMA, SSDP_DEVICE_SCHEMA)) icons = data.findall \ ("{%s}device/{%s}iconList/{%s}icon" \ % (SSDP_DEVICE_SCHEMA, SSDP_DEVICE_SCHEMA, SSDP_DEVICE_SCHEMA)) self._icons = [] for icon in icons: width = int(icon.findtext("{%s}width" % SSDP_DEVICE_SCHEMA)) height = int(icon.findtext("{%s}height" % SSDP_DEVICE_SCHEMA)) url = urljoin(self._location, icon.findtext("{%s}url" % SSDP_DEVICE_SCHEMA)) self._icons.append(IconInfo(width, height, url)) self._ready = True @handler("upnp_device_alive") def _on_device_alive \ (self, location, notification_type, max_age, server, usn): self._expiry_timer.interval = max_age self._expiry_timer.reset() @handler("upnp_device_bye_bye") def _on_device_bye_bye (self, usn): self._client.close() self.unregister() @property def usn(self): return getattr(self, "_usn", None) @property def location(self): return getattr(self, "_location", None) @property def ready(self): return getattr(self, "_ready", None) @property def friendly_name(self): return getattr(self, "_friendly_name", None) @property def icons(self): return copy(getattr(self, "_icons", None)) @property def valid_until(self): return self._expiry_timer._eTime \ if hasattr(self, "_expiry_timer") else None