Retagging 1.2.1 to include growlnotify Installer-package fix.
2 A Python module that enables posting notifications to the Growl daemon.
3 See <http://growl.info/> for more information.
6 __author__ = "Mark Rowe <bdash@users.sourceforge.net>"
7 __copyright__ = "(C) 2003 Mark Rowe <bdash@users.sourceforge.net>. Released under the BSD license."
8 __contributors__ = ["Ingmar J Stein (Growl Team)",
9 "Rui Carmo (http://the.taoofmac.com)",
10 "Jeremy Rossi <jeremy@jeremyrossi.com>",
11 "Peter Hosey <http://boredzo.org/> (Growl Team)",
21 GROWL_PROTOCOL_VERSION=1
22 GROWL_TYPE_REGISTRATION=0
23 GROWL_TYPE_NOTIFICATION=1
25 GROWL_APP_NAME="ApplicationName"
26 GROWL_APP_ICON="ApplicationIcon"
27 GROWL_NOTIFICATIONS_DEFAULT="DefaultNotifications"
28 GROWL_NOTIFICATIONS_ALL="AllNotifications"
29 GROWL_NOTIFICATIONS_USER_SET="AllowedUserNotifications"
31 GROWL_NOTIFICATION_NAME="NotificationName"
32 GROWL_NOTIFICATION_TITLE="NotificationTitle"
33 GROWL_NOTIFICATION_DESCRIPTION="NotificationDescription"
34 GROWL_NOTIFICATION_ICON="NotificationIcon"
35 GROWL_NOTIFICATION_APP_ICON="NotificationAppIcon"
36 GROWL_NOTIFICATION_PRIORITY="NotificationPriority"
38 GROWL_NOTIFICATION_STICKY="NotificationSticky"
40 GROWL_APP_REGISTRATION="GrowlApplicationRegistrationNotification"
41 GROWL_APP_REGISTRATION_CONF="GrowlApplicationRegistrationConfirmationNotification"
42 GROWL_NOTIFICATION="GrowlNotification"
43 GROWL_SHUTDOWN="GrowlShutdown"
44 GROWL_PING="Honey, Mind Taking Out The Trash"
45 GROWL_PONG="What Do You Want From Me, Woman"
46 GROWL_IS_READY="Lend Me Some Sugar; I Am Your Neighbor!"
49 growlPriority = {"Very Low":-2,"Moderate":-1,"Normal":0,"High":1,"Emergency":2}
52 """Builds a Growl Network Registration packet.
53 Defaults to emulating the command-line growlnotify utility."""
55 __notAllowed__ = [GROWL_APP_ICON, GROWL_NOTIFICATION_ICON, GROWL_NOTIFICATION_APP_ICON]
57 def __init__(self, hostname, password ):
58 self.hostname = hostname
59 self.password = password
60 self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
63 self.socket.sendto(data, (self.hostname, GROWL_UDP_PORT))
65 def PostNotification(self, userInfo):
66 if userInfo.has_key(GROWL_NOTIFICATION_PRIORITY):
67 priority = userInfo[GROWL_NOTIFICATION_PRIORITY]
70 if userInfo.has_key(GROWL_NOTIFICATION_STICKY):
71 sticky = userInfo[GROWL_NOTIFICATION_STICKY]
74 data = self.encodeNotify(userInfo[GROWL_APP_NAME],
75 userInfo[GROWL_NOTIFICATION_NAME],
76 userInfo[GROWL_NOTIFICATION_TITLE],
77 userInfo[GROWL_NOTIFICATION_DESCRIPTION],
80 return self.send(data)
82 def PostRegistration(self, userInfo):
83 data = self.encodeRegistration(userInfo[GROWL_APP_NAME],
84 userInfo[GROWL_NOTIFICATIONS_ALL],
85 userInfo[GROWL_NOTIFICATIONS_DEFAULT])
86 return self.send(data)
88 def encodeRegistration(self, application, notifications, defaultNotifications):
89 data = struct.pack("!BBH",
90 GROWL_PROTOCOL_VERSION,
91 GROWL_TYPE_REGISTRATION,
93 data += struct.pack("BB",
95 len(defaultNotifications) )
97 for i in notifications:
98 encoded = i.encode("utf-8")
99 data += struct.pack("!H", len(encoded))
101 for i in defaultNotifications:
102 data += struct.pack("B", i)
103 return self.encodePassword(data)
105 def encodeNotify(self, application, notification, title, description,
106 priority = 0, sticky = False):
108 application = application.encode("utf-8")
109 notification = notification.encode("utf-8")
110 title = title.encode("utf-8")
111 description = description.encode("utf-8")
112 flags = (priority & 0x07) * 2
116 flags = flags | 0x0001
117 data = struct.pack("!BBHHHHH",
118 GROWL_PROTOCOL_VERSION,
119 GROWL_TYPE_NOTIFICATION,
129 return self.encodePassword(data)
131 def encodePassword(self, data):
132 checksum = hashlib.md5()
133 checksum.update(data)
135 checksum.update(self.password)
136 data += checksum.digest()
139 class _ImageHook(type):
140 def __getattribute__(self, attr):
143 from _growlImage import Image
145 return getattr(Image, attr)
148 __metaclass__ = _ImageHook
150 class _RawImage(object):
151 def __init__(self, data): self.rawImageData = data
153 class GrowlNotifier(object):
155 A class that abstracts the process of registering and posting
156 notifications to the Growl daemon.
158 You can either pass `applicationName', `notifications',
159 `defaultNotifications' and `applicationIcon' to the constructor
160 or you may define them as class-level variables in a sub-class.
162 `defaultNotifications' is optional, and defaults to the value of
163 `notifications'. `applicationIcon' is also optional but defaults
164 to a pointless icon so is better to be specified.
167 applicationName = 'GrowlNotifier'
169 defaultNotifications = []
170 applicationIcon = None
171 _notifyMethod = _growl
173 def __init__(self, applicationName=None, notifications=None, defaultNotifications=None, applicationIcon=None, hostname=None, password=None):
175 self.applicationName = applicationName
176 assert self.applicationName, 'An application name is required.'
179 self.notifications = list(notifications)
180 assert self.notifications, 'A sequence of one or more notification names is required.'
182 if defaultNotifications is not None:
183 self.defaultNotifications = list(defaultNotifications)
184 elif not self.defaultNotifications:
185 self.defaultNotifications = list(self.notifications)
187 if applicationIcon is not None:
188 self.applicationIcon = self._checkIcon(applicationIcon)
189 elif self.applicationIcon is not None:
190 self.applicationIcon = self._checkIcon(self.applicationIcon)
192 if hostname is not None and password is not None:
193 self._notifyMethod = netgrowl(hostname, password)
194 elif hostname is not None or password is not None:
195 raise KeyError, "Hostname and Password are both required for a network notification"
197 def _checkIcon(self, data):
198 if isinstance(data, str):
199 return _RawImage(data)
204 if self.applicationIcon is not None:
205 self.applicationIcon = self._checkIcon(self.applicationIcon)
207 regInfo = {GROWL_APP_NAME: self.applicationName,
208 GROWL_NOTIFICATIONS_ALL: self.notifications,
209 GROWL_NOTIFICATIONS_DEFAULT: self.defaultNotifications,
210 GROWL_APP_ICON:self.applicationIcon,
212 self._notifyMethod.PostRegistration(regInfo)
214 def notify(self, noteType, title, description, icon=None, sticky=False, priority=None):
215 assert noteType in self.notifications
216 notifyInfo = {GROWL_NOTIFICATION_NAME: noteType,
217 GROWL_APP_NAME: self.applicationName,
218 GROWL_NOTIFICATION_TITLE: title,
219 GROWL_NOTIFICATION_DESCRIPTION: description,
222 notifyInfo[GROWL_NOTIFICATION_STICKY] = 1
224 if priority is not None:
225 notifyInfo[GROWL_NOTIFICATION_PRIORITY] = priority
228 notifyInfo[GROWL_NOTIFICATION_ICON] = self._checkIcon(icon)
230 self._notifyMethod.PostNotification(notifyInfo)