Remove trailing whitespace errors
[panucci.git] / src / panucci / services.py
blob8b56b1040d6a966729e133e65f87fdf7182bd645
1 #!/usr/bin/env python
3 # This file is part of Panucci.
4 # Copyright (c) 2008-2010 The Panucci Audiobook and Podcast Player Project
6 # Panucci is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # Panucci is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Panucci. If not, see <http://www.gnu.org/licenses/>.
19 # from services.py -- Core Services for gPodder
20 # Thomas Perl <thp@perli.net> 2007-08-24
22 # 2009-02-13 - nikosapi: made ObservableService more Panucci-esque
25 from __future__ import absolute_import
27 from logging import getLogger
29 # the following are only needed for __headphone_watcher
30 from panucci import util
31 import gobject
33 class ObservableService(object):
34 def __init__(self, signal_names=[], log=None):
35 self._log = getLogger('ObservableService') if log is None else log
37 self.observers = {}
38 for signal in signal_names:
39 self.observers[signal] = []
41 if not signal_names:
42 self._log.warning('No signal names defined...')
44 def register(self, signal_name, observer):
45 if signal_name in self.observers:
46 if not observer in self.observers[signal_name]:
47 self.observers[signal_name].append(observer)
48 self._log.debug( 'Registered "%s" as an observer for "%s"',
49 observer.__name__, signal_name )
50 else:
51 self._log.warning(
52 'Observer "%s" is already added to signal "%s"',
53 observer.__name__, signal_name )
54 else:
55 self._log.warning(
56 'Signal "%s" is not available for registration', signal_name )
58 def unregister(self, signal_name, observer):
59 if signal_name in self.observers:
60 if observer in self.observers[signal_name]:
61 self.observers[signal_name].remove(observer)
62 self._log.debug( 'Unregistered "%s" as an observer for "%s"',
63 observer.__name__, signal_name )
64 else:
65 self._log.warning(
66 'Observer "%s" could not be removed from signal "%s"',
67 observer.__name__, signal_name )
68 else:
69 self._log.warning(
70 'Signal "%s" is not available for un-registration.',
71 signal_name )
73 def notify(self, signal_name, *args, **kwargs):
74 caller = kwargs.get('caller')
75 caller = 'UnknownCaller' if caller is None else caller.__name__
76 rtn_value = False
78 if signal_name in self.observers:
79 self._log.debug(
80 'Sending signal "%s" for caller "%s"', signal_name, caller )
82 rtn_value = True
83 for observer in self.observers[signal_name]:
84 rtn_value &= bool(observer( *args ))
85 else:
86 self._log.warning(
87 'Signal "%s" (from caller "%s") is not available '
88 'for notification', signal_name, caller )
90 return rtn_value
93 class ForwardingObservableService(ObservableService):
94 """ An object that builds on ObservableService which provides a simple
95 way to forward/proxy a bunch of signals through an object of this
96 type. Ie: This object will call it's notify() method whenever a
97 forwarded signal from another object is emitted. """
99 def forward( self, from_object, signal_names, caller=None ):
100 """ Register signals to be forwarded
101 from_object: the object from which the signals will be emitted
102 signal_names: a string, list, or dict of signal names
103 caller: the caller to be passed to notify()
105 To forward a single signal, signal_names can just be a string
106 containing the name of the signal.
108 To forward many signals, signal_names can be a list of strings.
110 If you wish to change the name of the emitted signal (ie. if the
111 original object's signal is named "foo-bar" but you want this
112 object to emit "foo") you can do so by passing a dict to
113 signal_names. This dict must contain string pairs:
114 { "new name" : "from-object name", }
117 # bail if the from object isn't an ObservableService
118 if not isinstance( from_object, ObservableService ):
119 self._log.error( "Can't forward signals for "
120 "non-ObservableService type objects")
121 return
123 signals = {} # final list of signals to registered
125 if isinstance( signal_names, str ):
126 signals[signal_names] = signal_names
128 elif isinstance( signal_names, list ):
129 for name in signal_names:
130 signals[name] = name
132 elif isinstance( signal_names, dict ):
133 signals = signal_names
135 for to_name, from_name in signals.iteritems():
136 from_object.register( from_name, self._forward( to_name, caller ))
138 def _forward( self, emitted_signal_name, caller ):
139 """ Returns a function which calls notify() with the appropriate
140 parameters. """
142 def _callback( *args, **kwargs ):
143 kwargs["caller"] = caller
144 return self.notify( emitted_signal_name, *args, **kwargs )
146 return _callback
149 HEADPHONE_SYS = "/sys/devices/platform/gpio-switch/headphone/state"
151 class __headphone_watcher(ObservableService):
152 """ A small service with one singnal that reports whether or not the
153 headphones are connected. Returns True if the headphones are connected,
154 False if they're not and None if it's not possible to determine the
155 headphone status. """
157 signals = [ 'headphone-status-changed', ]
159 def __init__(self, sys_file):
160 self.__log = getLogger('panucci.serivces.__headphone_watcher')
161 ObservableService.__init__(self, self.signals, self.__log)
163 self.__is_connected = None
165 if util.platform.MAEMO and not util.platform.MAEMO5:
166 try:
167 self.__sys_file = open( sys_file, 'r' )
168 self.__is_connected = self.__get_state_from_fd(self.__sys_file)
169 gobject.io_add_watch( self.__sys_file, gobject.IO_PRI,
170 self.__on_status_changed )
171 except IOError:
172 self.__log.exception("Can't open headphone status file.")
174 @property
175 def is_connected(self):
176 return self.__is_connected
178 def __get_state_from_fd(self, fd):
179 fd.seek(0)
180 state = fd.read().strip()
181 return state == 'connected'
183 def __on_status_changed(self, src, cond):
184 self.__is_connected = self.__get_state_from_fd( src )
185 self.__log.debug(
186 'Headphone state changed (is_connected=%s).', self.__is_connected )
187 self.notify( 'headphone-status-changed', self.__is_connected,
188 caller=self.__on_status_changed )
189 return True
191 headphone_service = __headphone_watcher(HEADPHONE_SYS)