1
2
3
4
5 """ Control point side UPnP event support.
6 """
7
8 from xml.etree import ElementTree
9
10 from brisa.core import log, webserver
11 from brisa.core.threaded_call import run_async_function
12 from brisa.core.network import get_active_ifaces, get_ip_address
13
14
16 """ EventListener resource available at the control point web server,
17 listening for events.
18 """
19
21 """ Constructor for the EventListener class
22
23 @param observer: observer class with a _on_event() method.
24 """
25 webserver.CustomResource.__init__(self, 'eventSub')
26 self.observer = observer
27
29 """ Removes reference to observer to make GC easier """
30 self.observer = None
31
32 - def render(self, uri, request, response):
33 """ Event renderer method. As events come only on NOTIFY messages, this
34 method ignores any other type of message (GET, POST, ...).
35
36 @param uri: URI of the request
37 @param request: request object (Cherrypy)
38 @param response: response object (Cherrypy)
39
40 @type uri: string
41
42 @note: see Cherrypy documentation for further info about request and
43 response attributes and methods.
44 """
45 log.debug('Received render (%s)' % str((uri, request, response)))
46
47 if request.method == 'NOTIFY':
48 log.debug('Ok, got notify!')
49 self.render_NOTIFY(request, response)
50 else:
51 log.debug('Did not get notify, got %s' % request.method)
52
53 log.debug('Returning from render')
54
55 return ['']
56
58 """ Renders the notify message for an event.
59
60 @param request: request object (Cherrypy)
61 @param response: response object (Cherrypy)
62
63 @note: see Cherrypy documentation for further info about request and
64 response attributes and methods.
65 """
66 data = request.read()
67
68 data = data[data.find("<"):data.rfind(">")+1]
69
70 run_async_function(self.forward_notification, (request.headers, data),
71 0.0001)
72 return ""
73
75 """ Forwards notifications to the observer registered.
76
77 @param received_headers: headers received on the event notify
78 @param data: XML data for the event
79
80 @type received_headers: dictionary
81 @type data: string
82 """
83 log.debug('forward notification')
84 headers = {}
85 changed_vars = {}
86 for k, v in received_headers.items():
87 headers[k.lower()] = v
88
89 try:
90 tree = ElementTree.XML(data)
91 except:
92 log.debug('Event XML invalid: %s', data)
93 tree = None
94
95 if tree:
96 for prop1 in tree.findall('{%s}property' %
97 'urn:schemas-upnp-org:event-1-0'):
98
99 for prop2 in prop1:
100
101 changed_vars[prop2.tag] = prop2.text
102
103 log.debug('Event changed vars: %s', changed_vars)
104
105 if self.observer and 'sid' in headers:
106 self.observer._on_event(headers['sid'], changed_vars)
107
108 for id, dev in self.observer._known_devices.items():
109 service = self._find_service(dev, headers['sid'])
110 if service != None:
111 service._on_event(changed_vars)
112 return
113
115 """ Method to find a service with a specific subscription
116 id on the given device or on it children devices.
117
118 @param device: instance of a device
119 @param subscription_id: the id to compare with the service
120
121 @type device: RootDevice or Device
122 @type subscription_id: str
123
124 @return: if found, the service
125 @rtype: Service or None
126 """
127 for k, service in device.services.items():
128 if service.event_sid == subscription_id:
129 return service
130 for k, child_dev in device.devices:
131 service = self._find_service(child_dev, subscription_id)
132 if service:
133 return service
134 return None
135
136
138 """ EventListener server. Wraps BRisa's web server and listens for events.
139 """
140
142 """ Constructor for the EventListenerServer class.
143
144 @param observer: observer that implements the _on_event() method
145 """
146 self.srv = None
147 self.event_listener = EventListener(observer)
148
150 """ Returns a tuple in the form (host, port) where the server is being
151 hosted at.
152
153 @return: the host and port of the server host
154 @rtype: tuple
155 """
156 if not self.srv:
157 self.srv = webserver.WebServer()
158 self.srv.start()
159 return (self.srv.get_host(), self.srv.get_port())
160
161 - def start(self, event_host=None):
162 if not self.srv:
163 self.srv = webserver.WebServer()
164 self.srv.start()
165 if event_host:
166 self.srv.listen_url = 'http://%s:%d' % event_host
167 self.srv.add_resource(self.event_listener)
168
170 """ Stops the EventListenerServer. For restarting after stopping with
171 this method use EventListenerServer.srv.start().
172 """
173 if self.srv:
174 self.srv.stop()
175
177 if self.srv:
178 return self.srv.is_running()
179 else:
180 return False
181
186
188 self.srv = None
189 self.event_listener.cleanup()
190 self.event_listener = None
191