Alarm

From Enso Wiki

Jump to: navigation, search
  • NOTE: This command is written for Command Server not as a stand alone command

Contents

[edit] Information

Quick alarms scheduling. Alarm can be scheduled in relative time or in absolute time and optional text can be specified. At the time of alarm, sound is played and transparent message is displayed.

[edit] Usage

  • alarm {time [Text to display]}

Where time parameter can be absolute or relative time (see below) and text is optional.

  • Format can be:
    • Absolute:
       5       = 05:00AM
       1345    = 13:45
       13:45   = 13:45
       1:10a   = 01:10AM
       01:10am = 01:10AM
       10:20p  = 22:20
       10:20pm = 22:20
       10:20+ = 10:20AM tomorrow
       10:20p+ = 10:20PM tomorrow
    • Relative:
       10s     = in 10 seconds
       15m     = in 15 minutes
       2h      = in 2 hours
       1m30s   = in 1 minute and 30 seconds
       1h20s   = in 1 hour and 10 seconds
       1h20m   = in 1 hour and 20 minutes
       2h1m30s = in 2 hours and 1 minute and 30 seconds
  • Examples:
alarm 5m your tea is ready
alarm 5:15p hurry
alarm 30s thirty seconds
alarm 0915+ wakeup wakeup
alarm 1m30s

[edit] Installation

  • Create file command_alarm.py containing the code below. Put it into commands/ directory in Command Server directory.
  • Copy alarm.wav file into data/alarm/ directory in Command Server directory. If you don't like that, change it in _alarmNow method.


[edit] Code

from base import BaseCommand
import time
import threading
import re
 
class Command(BaseCommand):
    
    def __init__(self, *args):
        BaseCommand.__init__(self, *args)
 
        self.newArbitraryCommand("alarm {time [Text to display]}", self._alarm, "Quick alarm", "Quick alarm")
 
 
    def _alarm(self, postfix):
        if len(postfix) == 0:
            self.displayMessage("No time given!")
            return
        
        postfix = postfix.strip()
 
        if postfix.count(' ') > 0:
            alarmTime = postfix[:postfix.find(' ')]
            alarmText = postfix[postfix.find(' ')+1:]
        else:
            alarmTime = postfix
            alarmText = None
 
        if not re.search('^([0-9]{1,2}:[0-9]{1,2}|[0-9]{1,4})(am|a|pm|p)?\+?$', alarmTime) \
            and not re.search('^(([0-9]+h)?([0-9]+m)?([0-9]+s)?)$', alarmTime):
            self.displayMessage("Time has incorrect format!")
            return
 
        if self._isAbsoluteTime(alarmTime):
            absoluteTime = True
            atTime = self._parseAbsoluteTime(alarmTime)
        else:
            absoluteTime = False
            atTime = self._parseRelativeTime(alarmTime)
        
        if atTime and atTime[0]:
            now = time.time()
            secs = atTime[0] - now
            t = threading.Timer(secs, self._alarmNow, ["Alarm", alarmText])
            t.start()
            if absoluteTime:
                msg = "Alarm will sound at " + atTime[1] + atTime[2]
            else:
                msg = "Alarm will sound in " + atTime[1]
            self.displayMessage(msg)
 
 
    def _alarmNow(self, title, text):
        now = time.localtime(time.time())
        toDisplay = str(now[3]).rjust(2,'0') + ':' + str(now[4]).rjust(2,'0') + ':' + str(now[5]).rjust(2,'0')
        if not text:
            text = ""
        self.displayMessage(text, "Alarm " + toDisplay)
        self._playSound("data/alarm/alarm.wav")
        time.sleep(5)
 
 
    def _isAbsoluteTime(self, alarmTime):
        if alarmTime.endswith('+') \
            or alarmTime.endswith('a') \
            or alarmTime.endswith('p') \
            or alarmTime.endswith('am') \
            or alarmTime.endswith('pm') \
            or alarmTime.replace(':','').isdigit():
            return True
        return False
 
 
    """
    Parse absolute time
    Format can be:
        5       = 05:00AM
        1345    = 13:45
        13:45   = 13:45
        1:10a   = 01:10AM
        01:10am = 01:10AM
        10:20p  = 22:20
        10:20pm = 22:20
        10:20+ = 10:20AM tomorrow
        10:20p+ = 10:20PM tomorrow
    """
    def _parseAbsoluteTime(self, alarmTime):
        hours = 0
        minutes = 0
        isAM = False
        isPM = False
        isTomorrow = False
 
        # Strip off appendixes
        if alarmTime.endswith('+'):
            isTomorrow = True
            alarmTime = alarmTime[:-1]
 
        if alarmTime.endswith('a'):
            isAM = True
            alarmTime = alarmTime[:-1]
        elif alarmTime.endswith('am'):
            isAM = True
            alarmTime = alarmTime[:-2]
        elif alarmTime.endswith('p'):
            isPM = True
            alarmTime = alarmTime[:-1]
        elif alarmTime.endswith('pm'):
            isPM = True
            alarmTime = alarmTime[:-2]
        if len(alarmTime) == 0:
            self.displayMessage("Time has incorrect format!")
            return None
 
        # Parse actual time
        if alarmTime.find(':') > 0:
            # get hours
            try:
                hours = int(alarmTime[:alarmTime.find(':')])
            except:
                self.displayMessage("Hours: " + alarmTime[:alarmTime.find(':')], "Time has incorrect format!")
                return None
            # Get minutes
            try:
                minutes = int(alarmTime[alarmTime.find(':')+1:])
            except:
                self.displayMessage("Minutes: " + alarmTime[alarmTime.find(':')+1:], "Time has incorrect format!")
                return None
        else:
            if len(alarmTime) <= 2:
                # get hours
                try:
                    hours = int(alarmTime)
                except:
                    self.displayMessage("Hours: " + alarmTime, "Time has incorrect format!")
                    return None
                # Get minutes
                minutes = 0
            else:
                # get hours
                try:
                    hours = int(alarmTime[:2])
                except:
                    self.displayMessage("Hours: " + alarmTime[:2], "Time has incorrect format!")
                    return None
                # Get minutes
                try:
                    minutes = int(alarmTime[2:])
                except:
                    self.displayMessage("Minutes: " + alarmTime[2:], "Time has incorrect format!")
                    return None
 
        # Check hours and minutes validity
        if minutes < 0 or minutes > 59:
            self.displayMessage("Minutes must be 0-59: " + str(minutes), "Time has incorrect format!")
            return None
        if isPM or isAM:
            if hours < 1 or hours > 12:
                self.displayMessage("Hour must be 1-12: " + str(hours), "Time has incorrect format!")
                return None
        else:
            if hours < 0 or hours > 23:
                self.displayMessage("Hour must be 0-23: " + str(hours), "Time has incorrect format!")
                return None
 
        print str(hours) + ":" + str(minutes)
 
        if isAM or isPM:
            if isAM:
                ampm = "AM"
            else:
                ampm = "PM"
            atTime = time.strptime(str(hours) + " " + str(minutes) + " " + ampm, "%I %M %p")
        else:
            atTime = time.strptime(str(hours) + " " + str(minutes), "%H %M")
        print atTime
 
        now = time.localtime(time.time())
        
        if (not isTomorrow) and (atTime[3] < now[3] or (atTime[3] == now[3] and atTime[4] <= now[4])):
            self.displayMessage("Alarm can't be scheduled in the past", "Time has incorrect format!")
            return None
 
        year = now[0]
        month = now[1]
        day = now[2]
        hh = atTime[3]
        mm = atTime[4]
        ss = atTime[5]
        tz = now[8]
        if isTomorrow:
            day = day+1
        
        try:
            t = time.mktime((year, month, day, hh, mm, ss, atTime[6], atTime[7], tz))
        except OverflowError:
            print "OError"
            return None
        except ValueError:
            print "VError"
            return None
 
        parsedTimeStr = str(hours).rjust(2,'0') + ":" + str(minutes).rjust(2,'0')
        if isAM:
            parsedTimeStr = parsedTimeStr + "AM"
        elif isPM:
            parsedTimeStr = parsedTimeStr + "PM"
        
        if isTomorrow:
            append = " tomorrow"
        else:
            append = ""
 
        return t, parsedTimeStr, append
 
 
    """
    Parse relative time
    Format can be:
        10s     = in 10 seconds
        15m     = in 15 minutes
        2h      = in 2 hours
        1m30s   = in 1 minute and 30 seconds
        1h20s   = in 1 hour and 10 seconds
        1h20m   = in 1 hour and 20 minutes
        2h1m30s = in 2 hours and 1 minute and 30 seconds
    """
    def _parseRelativeTime(self, alarmTime):
        hours = 0
        minutes = 0
        seconds = 0
 
        if alarmTime.find('h') > 0:
            hourStr = alarmTime[:alarmTime.find('h')]
            if not hourStr.isdigit():
                self.displayMessage("Hours must be numeric", "Time has incorrect format!")
                return None
            hours = int(hourStr)
            if hours < 0:
                self.displayMessage("Hours count must be >= 0", "Time has incorrect format!")
                return None
            alarmTime = alarmTime[alarmTime.find('h')+1:]
        
        if alarmTime.find('m') > 0:
            minuteStr = alarmTime[:alarmTime.find('m')]
            if not minuteStr.isdigit():
                self.displayMessage("Minutes count must be numeric", "Time has incorrect format!")
                return None
            minutes = int(minuteStr)
            if minutes < 0:
                self.displayMessage("Minutes count must be >= 0", "Time has incorrect format!")
                return None
            alarmTime = alarmTime[alarmTime.find('m')+1:]
 
        if alarmTime.find('s') > 0:
            secondsStr = alarmTime[:alarmTime.find('s')]
            if not secondsStr.isdigit():
                self.displayMessage("Seconds count must be numeric", "Time has incorrect format!")
                return None
            seconds = int(secondsStr)
            if seconds < 0:
                self.displayMessage("Seconds count must be >= 0", "Time has incorrect format!")
                return None
 
        if hours + minutes + seconds == 0:
            self.displayMessage("Zero interval", "Time has incorrect format!")
            return None
 
        print str(hours) + ":" + str(minutes) + ":" + str(seconds)
 
        now = time.time()
        print now
 
        parsedTimeStr = ""
        if hours > 0:
            parsedTimeStr = parsedTimeStr + " " + str(hours) + " hours"
        if minutes > 0:
            parsedTimeStr = parsedTimeStr + " " + str(minutes) + " minutes"
        if seconds > 0:
            parsedTimeStr = parsedTimeStr + " " + str(seconds) + " seconds"
 
        return now + seconds + minutes*60 + hours*60*60, parsedTimeStr.strip()
 
    """
    Play alarm sound.
    Now support only WAV on Windows
    
    TODO: Other OS's
    """
    def _playSound(self, soundFile):
        try:
            # Try windows imports
            import winsound
            winsound.PlaySound(soundFile, winsound.SND_FILENAME)
        except:
            print "Not windows platform"
        
# vim:set tabstop=4 shiftwidth=4 expandtab

[edit] Notes

At the moment only WAV file can be played on alarm. You can change that in _alarmNow method.

[edit] Known issues

  • Alarm message is dismissed quickly when user is typing or moving the mouse when alarm goes off. There is no way in current developer prototype how to specify minimum time message should stay on screen, or how to be changed into mini message. Maybe in future releases.

[edit] Contact

Contact me in case of any ideas/bugs/suggestions --Blackdaemon 11:07, 11 February 2008 (PST)