Commits

Stefan Scherfke committed 24b921c

Fixed #16, fixed Growl not working. Growl messages for “keep alarming” are now sticky (and there will be only one of them).

Comments (0)

Files changed (7)

Tea Timer.wdgt/Scripts/Timer.js

 /**
- * This class implements the countdown timer and the alarm function.
+ * timer class implements the countdown timer and the alarm function.
  */
 function Timer() {
 	this._targetTime = null;
 Timer.prototype.run = function() {
     var h, m, s;
 
-    if (!this.isPaused) {
-		this.hours = $('#hours').text()
-		this.minutes = $('#minutes').text()
-		this.seconds = $('#seconds').text()
-		h = parseInt(this.hours, 10);
-		m = parseInt(this.minutes, 10);
-		s = parseInt(this.seconds, 10);
+    if (!timer.isPaused) {
+		timer.hours = $('#hours').text()
+		timer.minutes = $('#minutes').text()
+		timer.seconds = $('#seconds').text()
+		h = parseInt(timer.hours, 10);
+		m = parseInt(timer.minutes, 10);
+		s = parseInt(timer.seconds, 10);
     }
     else {
-        this.isPaused = false;
-        h = this.remainingHours;
-        m = this.remainingMinutes;
-        s = this.remainingSeconds;
+        timer.isPaused = false;
+        h = timer.remainingHours;
+        m = timer.remainingMinutes;
+        s = timer.remainingSeconds;
     }
 	m += h * 60;
 	s += m * 60;
-	this._targetTime = Date.parse(new Date()) + s * 1000;
-	this._isTargetReached = false;
+	timer._targetTime = Date.parse(new Date()) + s * 1000;
+	timer._isTargetReached = false;
 
 	// Check if target time has already been passed.
-	if (this._targetTime > Date.parse(new Date())) {
-		this.timerInterval = setInterval('timer.runTimer()', 1000);
+	if (timer._targetTime > Date.parse(new Date())) {
+		timer.timerInterval = setInterval('timer.runTimer()', 1000);
 	}
 	else {
 		front.resetDisplay();
  * Pause the countdown.
  */
 Timer.prototype.pause = function() {
-	clearInterval(this.timerInterval);
-	this.timerInterval = null;
-	this.isPaused = true
+	clearInterval(timer.timerInterval);
+	timer.timerInterval = null;
+	timer.isPaused = true
 };
 
 /**
  * Rewind the countdown – set it back to it initial value.
  */
 Timer.prototype.rewind = function() {
-	clearInterval(this.timerInterval);
-	this.timerInterval = null;
-	this._isTargetReached = false;
-	this.isPaused = false;
-	front.resetDisplayTo(this.hours, this.minutes, this.seconds);
+	clearInterval(timer.timerInterval);
+	timer.timerInterval = null;
+	timer._isTargetReached = false;
+	timer.isPaused = false;
+	front.resetDisplayTo(timer.hours, timer.minutes, timer.seconds);
 };
 
 /**
  * Stop the alarm.
  */
 Timer.prototype.stopAlarm = function() {
-    this.stopClicked = true;
+    timer.stopClicked = true;
 };
 
 
  * the displayed time and finally checks wheter to alarm the user or not.
  */
 Timer.prototype.runTimer = function() {
-    this.calculateRemainingTime();
+    timer.calculateRemainingTime();
     front.updateTimerDisplay();
-    this.checkForAlarm();
+    timer.checkForAlarm();
 };
 
 /**
  */
 Timer.prototype.calculateRemainingTime = function() {
 	currentTime = Date.parse(new Date());
-	this.remainingSeconds = (this._targetTime - currentTime) / 1000;
-	this.remainingMinutes = parseInt(this.remainingSeconds / 60);
-	this.remainingHours = parseInt(this.remainingMinutes / 60);
-	this.remainingSeconds %= 60;
-	this.remainingMinutes %= 60;
+	timer.remainingSeconds = (timer._targetTime - currentTime) / 1000;
+	timer.remainingMinutes = parseInt(timer.remainingSeconds / 60);
+	timer.remainingHours = parseInt(timer.remainingMinutes / 60);
+	timer.remainingSeconds %= 60;
+	timer.remainingMinutes %= 60;
 };
 
 /**
  * Check if the target time is reached and alarm the user.
  */
 Timer.prototype.checkForAlarm = function() {
-	if (this.remainingSeconds <= 0 && this.remainingMinutes <= 0
-			&& this.remainingHours <= 0 && !this._isTargetReached) {
-		clearInterval(this.timerInterval);
-		this.timerInterval = null;
-		this._isTargetReached = true;
-		this.isPaused = false;
-		front.resetDisplayTo(this.hours, this.minutes, this.seconds);
+	if (timer.remainingSeconds <= 0 && timer.remainingMinutes <= 0
+			&& timer.remainingHours <= 0 && !timer._isTargetReached) {
+		clearInterval(timer.timerInterval);
+		timer.timerInterval = null;
+		timer._isTargetReached = true;
+		timer.isPaused = false;
+		front.resetDisplayTo(timer.hours, timer.minutes, timer.seconds);
 		if (alarm.keepAlarming)
 			front.showStopAlarmButton();
-			this.stopClicked = false;
-        this.alarm();
+			timer.stopClicked = false;
+        timer.alarm();
 	}
 };
 
 /**
  * Alarm the user according to his preferences.
  *
- * If type is not set, all alarms set in alarm will be used. This is the case
- * for normal alarms. Else only the alarm specified via type will be used. This
+ * If type is not set, all alarms set in alarm will be used. timer is the case
+ * for normal alarms. Else only the alarm specified via type will be used. timer
  * is the case if the user switches is prefrences and a “preview alarm” is
  * raised.
  *
  *              "sound", "voice" or "growl". If not set, all alarm types will
  *              be used.
  */
-Timer.prototype.alarm = function(type) {
+Timer.prototype.alarm = function(type, isRepeating) {
 	var title = getLocalizedString('Tea Timer');
 	var message = $('#timerTarget').text();
+
 	if ($('#readyIn').text() == getLocalizedString('ready in'))
 	 	message += ' ' + getLocalizedString('is ready.');
 
         $('#soundPlayer').get(0).Play();
 	}
 	if (window.widget && (!type || type == 'growl') && alarm.growl) {
-		widget.system('/usr/bin/env python growl.py "' + title + '" "'
-				+ message + '"', function(){});
+		var sticky = false;
+		if (alarm.keepAlarming)
+			sticky = true;
+
+        if (!isRepeating) {
+            widget.system('/usr/bin/env python growl.py "' + title + '" "'
+				+ message + '" ' + sticky, function(){});
+        }
 	}
 	if (window.widget && (!type || type == 'voice') && alarm.voice != 'none') {
 		widget.system('/usr/bin/osascript -e \'say "' + message
 						+ '" using "' + alarm.voice + '"\'', function(){});
 	}
 
-	if (window.widget && alarm.keepAlarming && !type && !this.stopClicked) {
-		this.repeatAlarm();
+	if (window.widget && alarm.keepAlarming && !type && !timer.stopClicked) {
+		timer.repeatAlarm();
 	}
 };
 
  *
  */
 Timer.prototype.repeatAlarm = function() {
-	if (this.stopClicked) {
-		this._msCounter == 0;
+	if (timer.stopClicked) {
+		timer._msCounter == 0;
 		return;
 	}
-	this._msCounter = (this._msCounter + 10) % 5000;
-	if (this._msCounter == 0)
-		this.alarm();
+	timer._msCounter = (timer._msCounter + 10) % 5000;
+	if (timer._msCounter == 0)
+		timer.alarm(false, true);
 	else
-		setTimeout('this.repeatAlarm()', 10);
+		setTimeout('timer.repeatAlarm()', 10);
 };
 

Tea Timer.wdgt/growl.py

 #!/usr/bin/env python
 # encoding: utf-8
-
 """
-growl.py
-
-Notify the user via Growl that his tea (or what else) is ready. There is a
-fallback with AppleScript if the Growl python binding somehow can’t be imported.
+Notify the user via Growl that his tea (or what else) is ready.
 
 """
 
 import sys
 
 
-py_growl = False
-try:
-    from lib import Growl
-    py_growl = True
-except ImportError:
-	pass
-
-
-def py_notify(title, message, icon):
-    """Notifies the user via the Growl python binding."""
-    icon = Growl.Image.imageFromPath(icon)
-    notifier = Growl.GrowlNotifier(
-            applicationName = 'Tea Timer',
-            notifications = ['TimerTargetReached'],
-            applicationIcon = icon
-    )
-    notifier.register()
-    notifier.notify('TimerTargetReached', title, message)
-
-def as_notify(title, message, icon):
+def notify(title, message, icon, sticky):
     """Notifies the user via an AppleScript."""
     from os import getcwd
     from os.path import join
     import subprocess
 
     icon = join(getcwd(), icon)
-    cmd = ['/usr/bin/osascript', 'growl.scpt', title, message, icon]
+    cmd = ['/usr/bin/osascript', 'growl.scpt', title, message, icon, sticky]
     subprocess.call(cmd)
 
+
 def main(argv):
     """
     Notify the user via the Growl python binding if the module could be loaded
 
     """
     icon = 'Images/alarmclock.png'
-    fn = py_notify if py_growl else as_notify
-    fn(argv[1], argv[2], icon)
+    notify(argv[1], argv[2], icon, argv[3])
 
 
 if __name__ == '__main__':
-    if len(sys.argv) > 1:
-      exit(main(sys.argv))
-    else:
-      exit(2)
-	
+    exit(main(sys.argv))

Tea Timer.wdgt/growl.scpt

Binary file modified.

Tea Timer.wdgt/lib/Growl.py

-"""
-A Python module that enables posting notifications to the Growl daemon.
-See <http://growl.info/> for more information.
-"""
-__version__ = "0.7" 
-__author__ = "Mark Rowe <bdash@users.sourceforge.net>"
-__copyright__ = "(C) 2003 Mark Rowe <bdash@users.sourceforge.net>. Released under the BSD license."
-__contributors__ = ["Ingmar J Stein (Growl Team)", 
-					"Rui Carmo (http://the.taoofmac.com)",
-					"Jeremy Rossi <jeremy@jeremyrossi.com>",
-					"Peter Hosey <http://boredzo.org/> (Growl Team)",
-				   ]
-
-import _growl
-import types
-import struct
-import hashlib
-import socket
-
-GROWL_UDP_PORT=9887
-GROWL_PROTOCOL_VERSION=1
-GROWL_TYPE_REGISTRATION=0
-GROWL_TYPE_NOTIFICATION=1
-
-GROWL_APP_NAME="ApplicationName"
-GROWL_APP_ICON="ApplicationIcon"
-GROWL_NOTIFICATIONS_DEFAULT="DefaultNotifications"
-GROWL_NOTIFICATIONS_ALL="AllNotifications"
-GROWL_NOTIFICATIONS_USER_SET="AllowedUserNotifications"
-
-GROWL_NOTIFICATION_NAME="NotificationName"
-GROWL_NOTIFICATION_TITLE="NotificationTitle"
-GROWL_NOTIFICATION_DESCRIPTION="NotificationDescription"
-GROWL_NOTIFICATION_ICON="NotificationIcon"
-GROWL_NOTIFICATION_APP_ICON="NotificationAppIcon"
-GROWL_NOTIFICATION_PRIORITY="NotificationPriority"
-		
-GROWL_NOTIFICATION_STICKY="NotificationSticky"
-
-GROWL_APP_REGISTRATION="GrowlApplicationRegistrationNotification"
-GROWL_APP_REGISTRATION_CONF="GrowlApplicationRegistrationConfirmationNotification"
-GROWL_NOTIFICATION="GrowlNotification"
-GROWL_SHUTDOWN="GrowlShutdown"
-GROWL_PING="Honey, Mind Taking Out The Trash"
-GROWL_PONG="What Do You Want From Me, Woman"
-GROWL_IS_READY="Lend Me Some Sugar; I Am Your Neighbor!"
-
-	
-growlPriority = {"Very Low":-2,"Moderate":-1,"Normal":0,"High":1,"Emergency":2}
-
-class netgrowl:
-	"""Builds a Growl Network Registration packet.
-	   Defaults to emulating the command-line growlnotify utility."""
-
-	__notAllowed__ = [GROWL_APP_ICON, GROWL_NOTIFICATION_ICON, GROWL_NOTIFICATION_APP_ICON]
-
-	def __init__(self, hostname, password ):
-		self.hostname = hostname
-		self.password = password
-		self.socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
-
-	def send(self, data):
-		self.socket.sendto(data, (self.hostname, GROWL_UDP_PORT))
-		
-	def PostNotification(self, userInfo):
-		if userInfo.has_key(GROWL_NOTIFICATION_PRIORITY):
-			priority = userInfo[GROWL_NOTIFICATION_PRIORITY]
-		else:
-			priority = 0
-		if userInfo.has_key(GROWL_NOTIFICATION_STICKY):
-			sticky = userInfo[GROWL_NOTIFICATION_STICKY]
-		else:
-			sticky = False
-		data = self.encodeNotify(userInfo[GROWL_APP_NAME],
-								 userInfo[GROWL_NOTIFICATION_NAME],
-								 userInfo[GROWL_NOTIFICATION_TITLE],
-								 userInfo[GROWL_NOTIFICATION_DESCRIPTION],
-								 priority,
-								 sticky)
-		return self.send(data)
-
-	def PostRegistration(self, userInfo):
-		data = self.encodeRegistration(userInfo[GROWL_APP_NAME],
-									   userInfo[GROWL_NOTIFICATIONS_ALL],
-									   userInfo[GROWL_NOTIFICATIONS_DEFAULT])
-		return self.send(data)
-
-	def encodeRegistration(self, application, notifications, defaultNotifications):
-		data = struct.pack("!BBH",
-						   GROWL_PROTOCOL_VERSION,
-						   GROWL_TYPE_REGISTRATION,
-						   len(application) )
-		data += struct.pack("BB",
-							len(notifications),
-							len(defaultNotifications) )
-		data += application
-		for i in notifications:
-			encoded = i.encode("utf-8")
-			data += struct.pack("!H", len(encoded))
-			data += encoded
-		for i in defaultNotifications:
-			data += struct.pack("B", i)
-		return self.encodePassword(data)
-
-	def encodeNotify(self, application, notification, title, description,
-					 priority = 0, sticky = False):
-
-		application  = application.encode("utf-8")
-		notification = notification.encode("utf-8")
-		title		= title.encode("utf-8")
-		description  = description.encode("utf-8")
-		flags = (priority & 0x07) * 2
-		if priority < 0: 
-			flags |= 0x08
-		if sticky: 
-			flags = flags | 0x0001
-		data = struct.pack("!BBHHHHH",
-						   GROWL_PROTOCOL_VERSION,
-						   GROWL_TYPE_NOTIFICATION,
-						   flags,
-						   len(notification),
-						   len(title),
-						   len(description),
-						   len(application) )
-		data += notification
-		data += title
-		data += description
-		data += application
-		return self.encodePassword(data)
-
-	def encodePassword(self, data):
-		checksum = hashlib.md5()
-		checksum.update(data)
-		if self.password:
-		   checksum.update(self.password)
-		data += checksum.digest()
-		return data
-
-class _ImageHook(type):
-	def __getattribute__(self, attr):
-		global Image
-		if Image is self:
-			from _growlImage import Image
-
-		return getattr(Image, attr)
-
-class Image(object):
-	__metaclass__ = _ImageHook
-
-class _RawImage(object):
-	def __init__(self, data):  self.rawImageData = data
-
-class GrowlNotifier(object):
-	"""
-	A class that abstracts the process of registering and posting
-	notifications to the Growl daemon.
-
-	You can either pass `applicationName', `notifications',
-	`defaultNotifications' and `applicationIcon' to the constructor
-	or you may define them as class-level variables in a sub-class.
-
-	`defaultNotifications' is optional, and defaults to the value of
-	`notifications'.  `applicationIcon' is also optional but defaults
-	to a pointless icon so is better to be specified.
-	"""
-
-	applicationName = 'GrowlNotifier'
-	notifications = []
-	defaultNotifications = []
-	applicationIcon = None
-	_notifyMethod = _growl
-
-	def __init__(self, applicationName=None, notifications=None, defaultNotifications=None, applicationIcon=None, hostname=None, password=None):
-		if applicationName:
-			self.applicationName = applicationName
-		assert self.applicationName, 'An application name is required.'
-
-		if notifications:
-			self.notifications = list(notifications)
-		assert self.notifications, 'A sequence of one or more notification names is required.'
-
-		if defaultNotifications is not None:
-			self.defaultNotifications = list(defaultNotifications)
-		elif not self.defaultNotifications:
-			self.defaultNotifications = list(self.notifications)
-
-		if applicationIcon is not None:
-			self.applicationIcon = self._checkIcon(applicationIcon)
-		elif self.applicationIcon is not None:
-			self.applicationIcon = self._checkIcon(self.applicationIcon)
-
-		if hostname is not None and password is not None:
-			self._notifyMethod = netgrowl(hostname, password)
-		elif hostname is not None or password is not None:
-			raise KeyError, "Hostname and Password are both required for a network notification"
-
-	def _checkIcon(self, data):
-		if isinstance(data, str):
-			return _RawImage(data)
-		else:
-			return data
-
-	def register(self):
-		if self.applicationIcon is not None:
-			self.applicationIcon = self._checkIcon(self.applicationIcon)
-
-		regInfo = {GROWL_APP_NAME: self.applicationName,
-				   GROWL_NOTIFICATIONS_ALL: self.notifications,
-				   GROWL_NOTIFICATIONS_DEFAULT: self.defaultNotifications,
-				   GROWL_APP_ICON:self.applicationIcon,
-				  }
-		self._notifyMethod.PostRegistration(regInfo)
-
-	def notify(self, noteType, title, description, icon=None, sticky=False, priority=None):
-		assert noteType in self.notifications
-		notifyInfo = {GROWL_NOTIFICATION_NAME: noteType,
-					  GROWL_APP_NAME: self.applicationName,
-					  GROWL_NOTIFICATION_TITLE: title,
-					  GROWL_NOTIFICATION_DESCRIPTION: description,
-					 }
-		if sticky:
-			notifyInfo[GROWL_NOTIFICATION_STICKY] = 1
-
-		if priority is not None:
-			notifyInfo[GROWL_NOTIFICATION_PRIORITY] = priority
-
-		if icon:
-			notifyInfo[GROWL_NOTIFICATION_ICON] = self._checkIcon(icon)
-
-		self._notifyMethod.PostNotification(notifyInfo)

Tea Timer.wdgt/lib/__init__.py

Empty file removed.

Tea Timer.wdgt/lib/_growl.so

Binary file removed.

Tea Timer.wdgt/lib/_growlImage.so

Binary file removed.