Пишем консольный будильник на BASH-е

от автора

Задача «Проснуться утром» для меня, честно говоря, довольно сложная. Неделю назад пришла в голову идея: написать простой будильник, который будет проигрывать музыку все громче и громче пока не решишь математический пример.


В гугле есть некоторые статьи и темы, но все не то, что нужно.

Сразу оговорюсь, что компьютер (в моём случае ноутбук) ночью находится в спящем режиме, поэтому ему потребуется немного зарядки на ночь (у меня за ночь уходит около 30% батареи).
Если у вас настольный компьютер, есть перебои с электричеством и нет ИБП, то лучше не рискуйте.

Далее вы должны убедиться, что ваш компьютер поддерживает некоторые режимы «сна». Можете почитать об этом здесь.

Что нам потребуется?

— Утилита rtcwake — для засыпания компьютера (встроена в ядро).
— Утилита amixer — для постепенного увеличения громкости звука.
— Любой проигрыватель, позволяющий воспроизводить музыку в цикле (в моём случае mplayer).

Начнём

Зададим необходимые переменные:

# время по умолчанию tm='07:05'  # начальная громкость volume=10  # максимальная громкость volume_max=90  # время для смены задачи sec=2  # папка с музыкой folder=~username/Music/alarm/*  # временный файл для статуса temp=`mktemp -t alarm_status_XXX.txt` 

Здесь всё понятно.
«время для смены задачи» — время простоя перед генерацией новой задачи.
«папка с музыкой» — папка, из которой нужно брать список музыки. Я просто создал папку alarm и создал жёсткие ссылки на необходимые мне музыкальные композиции.
«временный файл для статуса» — нужен для завершения увеличения громкости. Если существует непустой файл, то завершаем фоновой процесс (кстати, можно просто через «jobs -l» найти наш процесс).

Теперь нужно написать функцию, которая будет убивать процессы mplayer-а и/или заново воспроизводить случайную музыку.

# включаем музыку alarm_start() { 	# убиваем все процессы mplayer-а 	jbs=(`ps al | grep [m]player | gawk -F ' ' '{print $3}'`) 	for job in ${jbs[*]} ; do 		kill -15 $jbs 	done 	 	# включаем случайную мелодию с бесконечным повтором 	if [ -z "$1" ] ; then 		mplayer -loop 0 -shuffle $folder &> /dev/null & 	fi } 

Также нужно перехватывать сигналы завершения процесса.

trap "echo -e '\nНеа, решите задачу!' && sleep 1 && alarm_start" SIGINT SIGTERM SIGHUP SIGQUIT SIGTSTP SIGSTOP 

Для того, чтобы можно было указать время просыпания будем использовать необязательный параметр $1. Сразу сделаем все проверки валидности даты.

if [[ $# > 0 ]] ; then 	if [[ "$1" == [0-9]:[0-9][0-9] ]] || [[ "$1" == [0-9][0-9]:[0-9][0-9] ]] ; then 		tm=$1 	else 		echo 'Установите правильное время. Пример: "07:00".' >&2 		exit 10 	fi fi  date1=$(date -d "`date +%m/%d/%y` $tm" +%s) date2=$(date -d "`date +%m/%d/%y` $tm tomorrow" +%s)  # последняя ошибка (если неверная дата) err=$? if [[ $err > 0 ]] ; then 	echo 'Установите правильное время. Пример: "07:00".' >&2 	exit $err fi  # если настоящее время больше времени для пробуждения, то ставим завтрашний день if [[ $date1 < `date -u +%s` ]] ; then 	date=$date2 else 	date=$date1 fi 

Переменные date1 и date2 нужны на случай, если пользователь укажет прошедшую дату, тогда время для просыпания будет установлено завтрашним днём. Если же время будет указано, например «07:99», то в переменную err будет занесён код ошибки.

Теперь можно «заставить» компьютер спать. rtcwake требует прав суперпользователя. Можно воспользоваться sudo. Всё, что вы делаете — вы делаете на свой страх и риск.

# засыпаем sudo rtcwake -m mem -t $date  # устанавливаем громкость amixer -q set Master $volume%  # включаем музыку alarm_start 

Про rtcwake, как уже говорил, вы можете прочитать здесь.

Осталось только сделать повышение громкости и пример для решения.

# повышаем уровень громкости while true ; do 	amixer sset Master 1%+ &> /dev/null 	volume=$(( $volume+1 )) 	 	if [ $volume -eq $volume_max ] ; then 		break 	elif [ -s "$temp" ] ; then 		rm "$temp" 		 		# возвращаем нормальную громкость 		amixer -q set Master 50% 		 		break 	fi 	sleep 2 done & 

Строчкой «amixer sset Master 1%+ &> /dev/null» мы указываем, что нужно повышать громкость на 1 процент и не нужно ничего выводить на экран.
Знак амперсанда & после оператора done нужен для того, чтобы увеличение громкости было в фоне.

И генерируем пример:

clear echo 'Чтобы выключить музыку решите пример:'  while true ; do 	# ждём 	echo "Ждите $sec сек." 	sleep $sec 	 	# пример который надо решить 	var1=$(( $RANDOM % 10000 - 5000 )) 	var2=$(( ($RANDOM % 100000 - 50000)/($RANDOM % 800 + 1) )) 	 	# операторы 	case $(( $RANDOM % 3 )) in 		0) 			opt='+' 			result=$(( $var1 + $var2 )) 		;; 		1) 			opt='-' 			result=$(( $var1 - $var2 )) 		;; 		2) 			opt='*' 			var2=$(( ($RANDOM % 5 + 5) )) 			result=$(( $var1 * $var2 )) 		;; 	esac 	 	# для красоты 	if [[ $var2 < 0 ]] ; then 		if [[ "$opt" == '-' ]] ; then 			opt='+' 			var2=$(( $var2 * -1 )) 		elif [[ "$opt" == '+' ]] ; then 			opt='-' 			var2=$(( $var2 * -1 )) 		fi 	fi 	 	# ответ 	read -p "$var1 $opt $var2 = " answer 	 	# завершаем цикл если ответ был правильный 	if [[ $answer == $result ]] ; then 		echo "Правильно! Ответ: $result." 		break 	else 		clear 		echo -n "Неверно! Правильный ответ был: $var1 $opt $var2 = $result." 		if [ -n "$answer" ] ; then 			echo " Вы ответили: $answer." 		else 			echo "" 		fi 	fi done  # параметром мы указываем, что не нужно воспроизводить музыку alarm_start false  # для выключения увеличения громкости echo "done" > "$temp" 

Вот и всё. Будильник был написан неделю назад. Телефонные будильники были выключены. За эту неделю я вполне мог проснуться пока решаю пример (задачка может быть очень простой (1020 — 120) или сложной (394 * 5). В одно утро мне попадался 13 раз подряд пример с умножением).

Можно создать alias в файле ~/.bashrc: «alias alarm=’~/path_to_script/alarm.sh’»
Для того, чтобы будильник нельзя было выключить закрытием программы консоли, я выхожу из текущего сеанса и переключаюсь в другую консоль (Alt+Ctrl+F1 или другой F[1-7]).
При желании будильник можно отключить подав сигнал принудительного закрытия, но для этого нужно будет зайти в другую консоль, авторизоваться, найти этот процесс и убить. Быстрее наверно решить пример 🙂

Полный код будильника

#!/bin/bash  # Будильник  # включаем музыку alarm_start() { 	# убиваем все процессы mplayer-а 	jbs=(`ps al | grep [m]player | gawk -F ' ' '{print $3}'`) 	for job in ${jbs[*]} ; do 		kill -15 $jbs 	done 	 	# включаем случайную мелодию с бесконечным повтором 	if [ -z "$1" ] ; then 		mplayer -loop 0 -shuffle $folder &> /dev/null & 	fi }  # время по умолчанию tm='07:05'  # начальная громкость volume=10  # максимальная громкость volume_max=90  # время для смены задачи sec=2  # папка с музыкой folder=~username/Music/alarm/*  # временный файл для статуса temp=`mktemp -t alarm_status_XXX.txt`  # от намеренного закрытия сонного человека trap "echo -e '\nНеа, решите задачу!' && sleep 1 && alarm_start" SIGINT SIGTERM SIGHUP SIGQUIT SIGTSTP SIGSTOP   if [[ $# > 0 ]] ; then 	if [[ "$1" == [0-9]:[0-9][0-9] ]] || [[ "$1" == [0-9][0-9]:[0-9][0-9] ]] ; then 		tm=$1 	else 		echo 'Установите правильное время. Пример: "07:00".' >&2 		exit 10 	fi fi  date1=$(date -d "`date +%m/%d/%y` $tm" +%s) date2=$(date -d "`date +%m/%d/%y` $tm tomorrow" +%s)  # последняя ошибка (если неверная дата) err=$? if [[ $err > 0 ]] ; then 	echo 'Установите правильное время. Пример: "07:00".' >&2 	exit $err fi  # если настоящее время больше времени для пробуждения, то ставим завтрашний день if [[ $date1 < `date -u +%s` ]] ; then 	date=$date2 else 	date=$date1 fi   # засыпаем sudo rtcwake -m mem -t $date # sudo echo "$date" > /sys/class/rtc/rtc0/wakealarm  # устанавливаем громкость amixer -q set Master $volume%  # день недели # day=$(( `date +%u` - 1 ))  # включаем музыку alarm_start  # повышаем уровень громкости while true ; do 	amixer sset Master 1%+ &> /dev/null 	volume=$(( $volume+1 )) 	 	if [ $volume -eq $volume_max ] ; then 		break 	elif [ -s "$temp" ] ; then 		rm "$temp" 		 		# возвращаем нормальную громкость 		amixer -q set Master 50% 		 		break 	fi 	sleep 2 done &  clear echo 'Чтобы выключить музыку решите пример:'  while true ; do 	# ждём 	echo "Ждите $sec сек." 	sleep $sec 	 	# пример который надо решить 	var1=$(( $RANDOM % 10000 - 5000 )) 	var2=$(( ($RANDOM % 100000 - 50000)/($RANDOM % 800 + 1) )) 	 	# операторы 	case $(( $RANDOM % 3 )) in 		0) 			opt='+' 			result=$(( $var1 + $var2 )) 		;; 		1) 			opt='-' 			result=$(( $var1 - $var2 )) 		;; 		2) 			opt='*' 			var2=$(( ($RANDOM % 5 + 5) )) 			result=$(( $var1 * $var2 )) 		;; 	esac 	 	# для красоты 	if [[ $var2 < 0 ]] ; then 		if [[ "$opt" == '-' ]] ; then 			opt='+' 			var2=$(( $var2 * -1 )) 		elif [[ "$opt" == '+' ]] ; then 			opt='-' 			var2=$(( $var2 * -1 )) 		fi 	fi 	 	# ответ 	read -p "$var1 $opt $var2 = " answer 	 	# завершаем цикл если ответ был правильный 	if [[ $answer == $result ]] ; then 		echo "Правильно! Ответ: $result." 		break 	else 		clear 		echo -n "Неверно! Правильный ответ был: $var1 $opt $var2 = $result." 		if [ -n "$answer" ] ; then 			echo " Вы ответили: $answer." 		else 			echo "" 		fi 	fi done  alarm_start false  # для выключения увеличения громкости echo "done" > "$temp" 

Для удобства код скопирован на pastebin, чтобы можно было скачать.

Надеюсь этот будильник заставит не проспать на учёбу или работу 🙂

ссылка на оригинал статьи http://habrahabr.ru/post/159987/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *