1
2
3
4
5 """ Provides a base control point class that can be extended to any specialized
6 control point.
7 """
8
9 from brisa.core import log
10 from brisa.upnp.ssdp import SSDPServer
11 from brisa.upnp.control_point.msearch import MSearch
12 from brisa.upnp.control_point.event import EventListenerServer
13 from brisa.upnp.control_point.device import Device
14
15
16 log = log.getLogger('control-point.basic')
17
18
20 """ This class implements UPnP Control Point functionalities.
21
22 The simplest way of using it is by subscribing for the device events,
23 starting it with start() and search for devices with start_search().
24 Note that after start() the control point will be already listening for
25 notifications. It will be listening for search responses only after
26 start_search().
27
28 Available events for subscription:
29 - new_device_event - triggered when a new device is found
30 - removed_device_event - triggered when a device announces its departure
31 - device_event - triggered when a device sends an event message
32
33 You may stop the control point anytime with stop() and it can be reused by
34 calling start() again. If you want to stop it definitely, you may use
35 destroy().
36 """
37 msg_already_started = 'tried to start() ControlPoint when already started'
38 msg_already_stopped = 'tried to stop() ControlPoint when already stopped'
39
40 - def __init__(self, receive_notify=True):
41 """ControlPoint class constructor.
42
43 @param receive_notify: if False, ignores notify messages from devices.
44 Default value is True and it can be set during runtime
45
46 @type receive_notify: boolean
47 """
48 self._ssdp_server = SSDPServer("BRisa Control Point", None,
49 receive_notify=receive_notify)
50 self._ssdp_server.subscribe("new_device_event", self._new_device_event)
51 self._ssdp_server.subscribe("removed_device_event",
52 self._removed_device_event)
53 self._msearch = MSearch(self._ssdp_server, start=False)
54 self._event_listener = EventListenerServer(self)
55 self.event_host = self._event_listener.host()
56 self._callbacks = {}
57 self._known_devices = {}
58
60 """ Returns a dict of devices found.
61 """
62 return self._known_devices
63
67
76
87
97
99 """ Cleanup references.
100 """
101 self._known_devices.clear()
102 self._msearch = None
103 self._ssdp_server = None
104 self._event_listener = None
105
107 """ Subscribes the callback for an event.
108
109 @param name: event name
110 @param callback: callback which will listen on the event
111
112 @type name: string
113 @type callback: callable
114 """
115 self._callbacks.setdefault(name, []).append(callback)
116
118 """ Unsubscribes the callback for an event.
119
120 @param name: event name
121 @param callback: callback which listens for the event
122
123 @type name: string
124 @type callback: callable
125 """
126 callbacks = self._callbacks.get(name, [])
127 if callback in callbacks:
128 callbacks.remove(callback)
129
130 - def start_search(self, interval, search_type="ssdp:all", reset=False):
131 """ Sends a multicast M-SEARCH message to discover UPnP devices.
132
133 @param interval: interval to wait between sending search messages
134 @param search_type: UPnP type search. Default value is "ssdp:all"
135 @param reset: clears the device list from any previous search
136
137 @type interval: float
138 @type search_type: string
139 @type reset: boolean
140 """
141 if reset:
142 self._ssdp_server.clear_device_list()
143 self._msearch.start(interval, search_type)
144
146 """ Stops the device search.
147 """
148 self._msearch.stop()
149
151 """ Forces a multicast MSearch bypassing the time interval. This method
152 force msearch to send discovery message, bypassing the initial time
153 interval passed to start_search function. Note this method doesn't
154 cause any effect if the start_search call was never called.
155
156 @param search_type: UPnP type search
157 @type search_type: string
158 """
159 self._msearch.double_discover(search_type)
160
162 """ Returns whether MSEARCH is running or not.
163
164 @return: Status of the MSearch
165 @rtype: boolean
166 """
167 return self._msearch.is_running()
168
170 """ GET function for the receive_notify property. Use
171 self.receive_notify instead.
172
173 @return: The receive_notify status
174 @rtype: boolean
175 """
176 return self._ssdp_server.receive_notify
177
179 """ SET function for the receive_notify property. Use
180 self.receive_notify instead.
181
182 @param n: The value to be set.
183 @type n: boolean
184 """
185 self._ssdp_server.receive_notify = n
186
187 receive_notify = property(_get_recv_notify,
188 _set_recv_notify,
189 doc='If False, the control point ignores NOTIFY\
190 messages from devices.')
191
193 """ Receives a new device event.
194
195 @param st: defines the device type
196 @param device_info: informations about the device
197
198 @type st: string
199 @type device_info: dict
200 """
201
202 if 'LOCATION' not in device_info:
203 return
204 Device.get_from_location_async(device_info['LOCATION'],
205 self._new_device_event_impl,
206 device_info)
207
209 """ Real implementation of the new device event handler.
210
211 @param device_info: informations about the device
212 @param device: the device object itself
213
214 @type device_info: dict
215 @type device: Device
216 """
217 if not device and self._ssdp_server:
218
219 self._ssdp_server.discovered_device_failed(device_info)
220 return
221
222 self._known_devices[device.udn] = device
223 self._callback("new_device_event", device)
224 log.info('Device found: %s' % device.friendly_name)
225
227 """ Receives a removed device event.
228
229 @param device_info: information about the device
230
231 @type device_info: dict
232 """
233 udn = device_info['USN'].split('::')[0]
234 if udn in self._known_devices:
235 log.info('Device is gone: %s' %
236 self._known_devices[udn].friendly_name)
237
238 self._known_devices.pop(udn, None)
239 self._callback("removed_device_event", udn)
240
242 """ Receives an event.
243
244 @param sid: Service id
245 @param changed_vars: Variables that have changed
246
247 @type sid: str
248 @type changed_vars: dict
249 """
250 self._callback("device_event", sid, changed_vars)
251
253 """ Callback for any event. Forwards the event to the subscribed
254 callbacks.
255
256 @param name: event name
257 @param args: arguments for the callbacks
258
259 @type name: string
260 @type args: tuple
261 """
262 for callback in self._callbacks.get(name, []):
263 callback(*args)
264