Advertisement
Guest User

Bash alarm

a guest
Oct 3rd, 2013
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 14.54 KB | None | 0 0
  1. #!/bin/bash
  2.  
  3. # Requires mph123 rtcwake notify-send amixer
  4.  
  5.  
  6. LOW_VOLUME=20
  7. HIGH_VOLUME=30 #64
  8. VOLUME_STEP_DURATION=1s
  9. DEFAULT_ALARM_SONG='/home/lukaschmela/Hudba/Doobie Brothers - Long train running.mp3'
  10. STANDBY_TIMEOUT=180
  11. CRON_FINGERPRINT='Alarm daemon fingerprint - DO NOT REMOVE!'
  12. WAIT_FOR_PID=true
  13. WAIT_FOR_PID_MAX=3
  14.  
  15. CONFIG_FILE=/etc/alarm.conf
  16. ALARM_LIST_FILE=/etc/alarm.list
  17. ACTIVE_ALARM_FILE=/etc/alarm.active
  18. PID_FILE=/dev/shm/alarm.pid
  19.  
  20. if [ ! -f "$CONFIG_FILE" ]; then
  21.     echo "VAROVÁNÍ! Konfigurační soubor neexistuje, budou použity výchozí hodnoty."
  22. else
  23.     . $CONFIG_FILE
  24. fi
  25.  
  26. # 0      :1        :2           :3                     :4                :5                          :6                 :7         :8
  27. # enabled:timestamp:song_seconds:number_of_song_repeats:repeat_song_after:repeat_alarm_how_many_times:repeat_alarm_after:alarm_name:song
  28. #ALARMS="1:1378931426:0:0:0:-1:86400:Testovací budík:"
  29. #ALARM="1:1378921426:3:0:3:-1:86400:Večerní budík:"
  30.  
  31.  
  32.  
  33. volume () { # Syntax: volume [set|get] [mixer_ID] [value] [unmute]
  34.     amixer -c0 $1 $2 $3 # > /dev/null
  35. }
  36.  
  37. #alarm_task () {
  38. #    touch "$LOCK_FILE"
  39. #    mpg123 "$alarm_song" > /dev/null 2>&1
  40. #    rm "$LOCK_FILE"
  41. #}
  42.  
  43. parse_alarm () {
  44.     IFS=':' read -a alarm <<< "$1"
  45.     alarm_timestamp=${alarm[1]}
  46.     echo $1 # FIXME
  47.     echo "Timestamp: ${alarm[1]}"
  48.    
  49.     if [[ ${alarm[2]=0} != *[!0-9]* && ${alarm[2]} -lt 1 ]]; then # Default to play whole song
  50.         alarm[2]=0
  51.     fi
  52.    
  53.     if [ ${alarm[3]=1} -lt 1 ]; then # Safeguard for number of song repeats
  54.         alarm[3]=2147483647 # 2^31 - 1
  55.     fi
  56.    
  57.     if [ ${alarm[4]=0} -lt 0 ]; then # Default pause between song repeatings is 0s
  58.         alarm[4]=0
  59.     fi
  60.    
  61.     if [ ${alarm[5]=0} -lt 0 ]; then # Safeguard for number of alarm repeats FIXME OK?
  62.         alarm[5]=2147483647 # 2^31 - 1
  63.     fi
  64.     alarm_song="${alarm[8]:=$DEFAULT_ALARM_SONG}"
  65.     echo "Písnička: $alarm_song"
  66. }
  67.  
  68. notify () {
  69.     echo "Spouštím alarm ${alarm[7]}"
  70.     notify-send -i '/usr/share/icons/gnome/32x32/status/stock_appointment-reminder.png' "Budík ${alarm[7]}" "`date -d @${alarm[1]} +%c`"
  71. }
  72.  
  73. __ensure_root () {
  74.     if [ `id -u` -ne 0 ]; then
  75.         echo 'CHYBA! Služba musí být spuštěna pod superuživatelem.'
  76.         exit 1
  77.     fi
  78. }
  79.  
  80. __alarm_running () {
  81.     # return [ -f $LOCK_FILE ]
  82.     ps -o pid= -p $alarm_PID > /dev/null 2>&1
  83.     [ $? -eq 1 ] && return 1 || return 0
  84. }
  85. __stop_alarm () {
  86.     kill -INT $alarm_PID > /dev/null 2>&1
  87.     return $?
  88. }
  89.  
  90. __toggle_alarm () {
  91.     sed -i "$(($1+1))s/[01]\(.*\)/$2\1/g" "$ALARM_LIST_FILE"
  92.     return $?
  93. }
  94.  
  95. __generate_cron_entry () {
  96.     echo "`date -d @$1 '+%M %H %d %m'` * $0 start # $CRON_FINGERPRINT"
  97. }
  98.  
  99. __regex_escape () {
  100.     echo "$1" | sed -e 's/[]\/()$*.^|[]/\\&/g'
  101. }
  102.  
  103. __alarm_syntax_check () {
  104.     if [ `echo "$1" | wc -l` -ne 1 -o `str="${1//[^:]/}"; echo ${#str}` -ne 8 ]; then
  105.         echo "SYNTAKTICKÁ CHYBA! Ujistěte se, že jste nastavení budíku uzavřeli do uvozovek."
  106.         exit 3
  107.     fi
  108. }
  109.  
  110. __set_active_alarm () {
  111.     IFS=':'; echo "${alarm[*]}" > "$ACTIVE_ALARM_FILE"
  112.     chmod 600 "$ACTIVE_ALARM_FILE"
  113.     chown root:root "$ACTIVE_ALARM_FILE"
  114. }
  115.  
  116. __set_rtc_wakeup () {
  117.     __set_active_alarm
  118.     rtcwake -m no -l -t ${alarm[1]}
  119.  
  120.     #echo 0 > /sys/class/rtc/rtc0/wakealarm
  121.     #echo `date '+%s' -d @$1` > /sys/class/rtc/rtc0/wakealarm
  122. }
  123.  
  124. __remove_rtc_wakeup () {
  125.         echo "" > "$ACTIVE_ALARM_FILE"
  126.         rtcwake -m disable
  127. }
  128.  
  129. __update_alarm_schedule () {
  130.     local next_alarm[1]=2147483647 # the greatest timestamp (2^31 - 1)
  131.     #local alarm_index=-1
  132.     local repeats_needed
  133.     local current_timestamp=`date +%s`
  134.  
  135.     for ((i=0; i<${#ALARMS[*]}; i++)); do
  136.         if [ "${ALARMS[$i]:0:1}" == "1" ]; then # Check if alarm is enabled.
  137.             IFS=':' read -a array <<< "${ALARMS[$i]}"
  138.             if [ ${next_alarm[1]} -gt ${array[1]} ]; then
  139.                 if [ ${array[1]} -gt $current_timestamp ]; then # Found one of next alarms - save temporary information and check if it is the earliest.
  140.                     #alarm_index=$i
  141.                     IFS=':' read -a next_alarm <<< "${ALARMS[$i]}"
  142.                 else
  143.                     if [ ${array[5]=0} == 0 ]; then # This alarm is outdated - disable.
  144.                         __toggle_alarm $i 0
  145.                         continue
  146.                     else
  147.                         repeats_needed=$(( ( $current_timestamp - ${array[1]} - 1 + ${array[6]} ) / ${array[6]} )) # Number of repeats needed for the alarm to be set in future (rounded up)
  148.                        
  149.                         if [ ${array[5]} -lt 0 -o ${array[5]} -ge $repeats_needed ]; then # Found one of next alarms - save temporary information and check if it is the earliest.
  150.                             IFS=':' read -a next_alarm <<< "${ALARMS[$i]}"
  151.                             let next_alarm[1]+=$(( ${array[6]} * $repeats_needed )) # Update the timestamp so that we don't get confused
  152.                             let next_alarm[5]-=repeats_needed
  153.                             break
  154.                         else
  155.                             __toggle_alarm $i 0 # Outdated - disable.
  156.                         fi
  157.                     fi
  158.                 fi
  159.             fi
  160.         fi
  161.     done
  162.    
  163.     unset array
  164.    
  165.     if [ -z "${next_alarm[0]}" ]; then # No alarms left - exiting.
  166.         __remove_rtc_wakeup
  167.         echo "Nejsou nastaveny žádné alarmy - končím."
  168.         exit 0
  169.     fi
  170.  
  171.     ALARM=$( IFS=':'; echo "${next_alarm[*]}" )
  172. }
  173.  
  174. __update_alarm_list_and_start () {
  175.     ALARMS=`cat "$ALARM_LIST_FILE"`
  176.     __update_alarm_schedule
  177.     __set_active_alarm
  178.     $0 start
  179.     exit $?
  180. }
  181.  
  182. alarm_task() {
  183.     if ( set -o noclobber; echo $BASHPID > "$PID_FILE" ) 2> /dev/null; then
  184.         chmod 400 "$PID_FILE"
  185.         chown root:root "$PID_FILE"
  186.         trap "rm -f \"$PID_FILE\"" INT TERM EXIT
  187.     else
  188.         exit 255
  189.     fi
  190.    
  191.     ALARM=`cat "$ACTIVE_ALARM_FILE"`
  192.     ALARMS=`cat "$ALARM_LIST_FILE"`
  193.    
  194.     if [ -z "$ALARM" ]; then
  195.         __update_alarm_schedule
  196.     fi
  197.    
  198.    
  199.     while true; do
  200.         parse_alarm "$ALARM"
  201.        
  202.         cron_used=$([ -n "`crontab -l | grep "# $(__regex_escape "$CRON_FINGERPRINT")"`" ] && echo true || echo false)
  203.  
  204.         current_timestamp=`date +%s`
  205.         if [ $alarm_timestamp -gt $current_timestamp ]; then # Wait until the time specified by the user (in %H%M format) to wake up.
  206.             if (( $alarm_timestamp - $current_timestamp > $STANDBY_TIMEOUT )); then # Do not wait that long - create a cron job instead and exit.
  207.                 if ! $cron_used; then # Safe to create a cron job - none exists within the schedule table.
  208.                     crontab -l | { cat; __generate_cron_entry $alarm_timestamp; } | crontab -
  209.                 else # Our cron job is already created - update it.
  210.                     crontab -l | { sed -r "s/^.*# $(__regex_escape "$CRON_FINGERPRINT")$/$(__regex_escape "$(__generate_cron_entry $alarm_timestamp)")/g"; } | crontab -
  211.                fi
  212.                __set_rtc_wakeup
  213.                exit 4 # Exit for now - cron will call us when needed
  214.            else # We will wait this little time.
  215.                $cron_used && crontab -l | { sed "/^.*# $(__regex_escape "$CRON_FINGERPRINT")$/d"; } | crontab - # Delete existing cron job.
  216.                 #echo "Sleeping for $(( $alarm_timestamp - `date +%s` )) seconds."
  217.                 sleep $(( $alarm_timestamp - `date +%s` ))
  218.             fi
  219.         fi
  220.  
  221.  
  222.         # In case we are connected to an amplifier.
  223.         volume set PCM 255 unmute
  224.        
  225.         # We can start scrobbling again.
  226.         volume_output=`volume set Master $LOW_VOLUME unmute` # Initial value.
  227.  
  228.         # Alarm loop.
  229.  
  230.         user_woken=false
  231.         i=0
  232.         while true; do
  233.             notify
  234.             mpg123 "$alarm_song" > /dev/null 2>&1 &
  235.             alarm_PID=$!
  236.             trap "__stop_alarm" INT TERM EXIT
  237.             #alarm_task > /dev/null 2>&1 &
  238.             #alarm_task_PID=$!
  239.             #sleep 1
  240.             #alarm_PID=`ps -o pid= -o comm= --ppid $alarm_task_PID | grep mpg123`
  241.            
  242.             if [ ${alarm[2]} -ne 0 ]; then
  243.                 sleep ${alarm[2]} && __stop_alarm &
  244.             fi
  245.  
  246.             # Gracefully increase the volume of the music until user wakes up.
  247.             for ((vol=$LOW_VOLUME; vol<=$HIGH_VOLUME; vol++)); do
  248.                 echo $vol # FIXME
  249.                 if ! __alarm_running; then
  250.                     break;
  251.                 elif [ "$volume_output" != "`volume get Master`" ]; then # User woken up!
  252.                     __stop_alarm
  253.                     user_woken=true
  254.                     break
  255.                 fi
  256.                 volume_output=`volume set Master $vol`
  257.                 sleep $VOLUME_STEP_DURATION
  258.             done # Music turned up or ended.
  259.  
  260.             # Wait for user to wake up (change volume) or music to end.
  261.             if ! $user_woken; then # User stil sleeps!
  262.                 while [ "$volume_output" == "`volume get Master`" ]; do # Spy volume level to find out if user has finally woken up.
  263.                     if ! __alarm_running; then # Song ended - repeat?
  264.                         let i++
  265.                         if [ $i -lt ${alarm[3]} ]; then
  266.                             sleep ${alarm[4]}
  267.                             if [ "$volume_output" == "`volume get Master`" ]; then # User haven't managed to get up during the pause.
  268.                                 continue 2
  269.                             else
  270.                                 break 2
  271.                             fi
  272.                         else
  273.                             break 2 # No repeatings left, give up.
  274.                         fi
  275.                     fi
  276.                     sleep 1
  277.                 done
  278.             #else
  279.             #    echo "woken!"
  280.             fi
  281.  
  282.             __stop_alarm # Volume changed - user woken up!
  283.             break
  284.         done
  285.        
  286.         __update_alarm_schedule
  287.     done
  288. }
  289.  
  290. case $1 in
  291.     start)
  292.         __ensure_root
  293.         if [ -f $PID_FILE ]; then
  294.             pid=`cat $PID_FILE`
  295.             if [ $(ps -o pid= -p $pid > /dev/null 2>&1; echo $?) -eq 0 ]; then
  296.                 echo "CHYBA! Služba již běží jako proces $pid."
  297.                 exit 2
  298.             else
  299.                 rm -f "$PID_FILE"
  300.             fi
  301.         fi
  302.         alarm_task #&
  303.         task_pid=$!
  304.         if $WAIT_FOR_PID; then
  305.             for ((i=0; i<$WAIT_FOR_PID_MAX; i++)); do
  306.                 if [ -f "$PID_FILE" ]; then
  307.                     if [ `cat "$PID_FILE"` -eq $task_pid ]; then
  308.                         exit 0
  309.                     else
  310.                         echo "CHYBA! Služba již běží jako proces $pid."
  311.                         exit 2
  312.                     fi
  313.                 fi
  314.             done
  315.             echo "CHYBA! Službu se nepodařilo spustit."
  316.             exit 3
  317.         fi
  318.         exit 0
  319.         ;;
  320.     stop)
  321.         __ensure_root
  322.         if [ -f $PID_FILE ]; then
  323.             pid=`cat $PID_FILE`
  324.             if [ $(ps -o pid= -p $pid > /dev/null 2>&1; echo $?) -eq 0 ]; then
  325.                 kill $pid > /dev/null 2>&1
  326.                 if [ $? -ne 0 ]; then
  327.                     echo 'CHYBA! Službu se nepodařilo vypnout.'
  328.                     exit 2
  329.                 fi
  330.                 exit 0
  331.             fi
  332.         fi
  333.         ;;
  334.     restart)
  335.         $0 stop && $0 start
  336.         exit $?
  337.         ;;
  338.     list)
  339.         echo -e "\e[1;39mID  Alarm\e[0m"
  340.         id=0
  341.         while read line; do
  342.             echo "$id $line"
  343.             ((id++))
  344.         done < "$ALARM_LIST_FILE"
  345.         ;;
  346.     add)
  347.         __ensure_root
  348.         if [ -z "$2" ]; then
  349.             echo "Použití: $0 add nastavení_budíku"
  350.             exit 2
  351.         fi
  352.         __alarm_syntax_check "$2"
  353.         $0 stop
  354.         if [ $? -ne 0 ]; then
  355.             exit 4
  356.         fi
  357.        
  358.         echo "$2" >> "$ALARM_LIST_FILE"
  359.         #sed -i '/^$/d' "$ALARM_LIST_FILE" # Remove all empty lines.
  360.         __update_alarm_list_and_start
  361.         ;;
  362.     remove)
  363.         __ensure_root
  364.         # TODO Matching by alarm name
  365.         if [ -z "$2" ]; then
  366.             echo "Použití: $0 remove ID_budíku"
  367.             exit 2
  368.         fi
  369.         $0 stop
  370.         if [ $? -ne 0 ]; then
  371.             exit 3
  372.         fi
  373.        
  374.         sed -i "$(($2+1))d" "$ALARM_LIST_FILE"
  375.         #sed -i '/^$/d' "$ALARM_LIST_FILE" # Remove all empty lines.
  376.         cat "$ALARM_LIST_FILE"
  377.         __update_alarm_list_and_start
  378.         ;;
  379.     update)
  380.         __ensure_root
  381.         # TODO Matching by alarm name
  382.         if [ -z "$2" -o -z "$3" ]; then
  383.             echo "Použití: $0 update ID_budíku nastavení_budíku"
  384.             exit 2
  385.         fi
  386.         __alarm_syntax_check "$3"
  387.         $0 stop
  388.         if [ $? -ne 0 ]; then
  389.             exit 4
  390.         fi
  391.        
  392.         sed -i "$(($2+1))s/.*/$(__regex_escape "$3")/g" "$ALARM_LIST_FILE"
  393.         #sed -i '/^$/d' "$ALARM_LIST_FILE" # Remove all empty lines.
  394.         __update_alarm_list_and_start
  395.         ;;
  396.     enable)
  397.         __ensure_root
  398.         # TODO Matching by alarm name
  399.         if [ -z "$2" ]; then
  400.             echo "Použití: $0 enable ID_budíku"
  401.             exit 2
  402.         fi
  403.         $0 stop
  404.         if [ $? -ne 0 ]; then
  405.             exit 3
  406.         fi
  407.        
  408.         __toggle_alarm $2 1 || echo "CHYBA! Možná jste zadali neplatné ID budíku."
  409.         #sed -i '/^$/d' "$ALARM_LIST_FILE" # Remove all empty lines.
  410.         __update_alarm_list_and_start
  411.         ;;
  412.     disable)
  413.         __ensure_root
  414.         # TODO Matching by alarm name
  415.         if [ -z "$2" ]; then
  416.             echo "Použití: $0 disable ID_budíku"
  417.             exit 2
  418.         fi
  419.         $0 stop
  420.         if [ $? -ne 0 ]; then
  421.             exit 3
  422.         fi
  423.        
  424.         __toggle_alarm $2 0 || echo "CHYBA! Možná jste zadali neplatné ID budíku."
  425.         #sed -i '/^$/d' "$ALARM_LIST_FILE" # Remove all empty lines.
  426.         __update_alarm_list_and_start
  427.         ;;
  428.     install)
  429.         touch "$CONFIG_FILE" "$ACTIVE_ALARM_FILE" "$ALARM_LIST_FILE"
  430.         chmod 644 "$CONFIG_FILE" "$ALARM_LIST_FILE"
  431.         chmod 600 "$ACTIVE_ALARM_FILE"
  432.         chown root:root "$CONFIG_FILE" "$ACTIVE_ALARM_FILE" "$ALARM_LIST_FILE"
  433.         ;;
  434.     help|h|--help|-h)
  435.         echo "Použití: $0 [start|restart|stop|list|add|remove|update|enable|disable] [volby]"
  436.         ;;
  437.     *)
  438.         if [ -n "$1" ]; then
  439.             echo "Neznámý přepínač \"$1\"\n"
  440.         fi
  441.         $0 help
  442.         exit 3
  443. esac
  444.  
  445. exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement