Package brisa :: Package upnp :: Package control_point :: Module control_point
[hide private]
[frames] | no frames]

Source Code for Module brisa.upnp.control_point.control_point

  1  # Licensed under the MIT license 
  2  # http://opensource.org/licenses/mit-license.php or see LICENSE file. 
  3  # Copyright 2007-2008 Brisa Team <brisa-develop@garage.maemo.org> 
  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   
19 -class ControlPoint(object):
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
59 - def get_devices(self):
60 """ Returns a dict of devices found. 61 """ 62 return self._known_devices
63
64 - def is_running(self):
65 return self._ssdp_server.is_running() and \ 66 self._event_listener.is_running()
67
68 - def start(self):
69 """ Starts the control point. 70 """ 71 if not self.is_running(): 72 self._ssdp_server.start() 73 self._event_listener.start(self.event_host) 74 else: 75 log.warning(self.msg_already_started)
76
77 - def stop(self):
78 """ Stops the control point. 79 """ 80 if self.is_running(): 81 if self.is_msearch_running(): 82 self.stop_search() 83 self._ssdp_server.stop() 84 self._event_listener.stop() 85 else: 86 log.warning(self.msg_already_stopped)
87
88 - def destroy(self):
89 """ Destroys and quits the control point definitely. 90 """ 91 if self.is_running(): 92 self.stop() 93 self._msearch.destroy() 94 self._ssdp_server.destroy() 95 self._event_listener.destroy() 96 self._cleanup()
97
98 - def _cleanup(self):
99 """ Cleanup references. 100 """ 101 self._known_devices.clear() 102 self._msearch = None 103 self._ssdp_server = None 104 self._event_listener = None
105
106 - def subscribe(self, name, callback):
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
117 - def unsubscribe(self, name, callback):
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
145 - def stop_search(self):
146 """ Stops the device search. 147 """ 148 self._msearch.stop()
149
150 - def force_discovery(self, search_type="ssdp:all"):
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
161 - def is_msearch_running(self):
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
169 - def _get_recv_notify(self):
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
178 - def _set_recv_notify(self, n):
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
192 - def _new_device_event(self, st, device_info):
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 # Callback assigned for new device event, processes asynchronously 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
208 - def _new_device_event_impl(self, device_info, device):
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 # Device creation failed, tell SSDPSearch to forget it 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
226 - def _removed_device_event(self, device_info):
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
241 - def _on_event(self, sid, changed_vars):
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
252 - def _callback(self, name, *args):
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