Как настроить «Службу точного времени» с использованием Asterisk 16

Мне для моей телефонии нужно разобрать, как заставить или научить Asterisk 16 проговаривать звонок на номер, где цель узнать, как раньше, а может и сейчас есть у тех кто использует городской телефон при звонке на номер 1000 с целью узнать точное время. Помню, я когда был маленький то частенько на дисковом телефоне набирал номер 1000 чтобы услышать: «Точное время 10 часов 10 минут 10 секунд». А разобрав текущую задачу я смогу адаптировать ее и для голосового уведомления баланса на своем номере «Мегафон» и «Теле2».

В роли сервера где развернут сервис телефонии у меня выступает Ubuntu 18.04 Server amd64.

Первым делом нужно проверить, а если не сделано ранее настроить чтобы в системе было точное синхронизированное время и правильный часовой пояс. Хотя все это делает мой скрипт который я при разворачивании сервисов на Ubuntu выкачиваю со своего хранилища Mercurial, затем запускаю. В итоге получаю полностью настроенную систему.

Настройки АТС:

ekzorchik@srv-asterisk:~$ sudo cp /etc/asterisk/sip.conf /etc/asterisk/sip.conf.backup

ekzorchik@srv-asterisk:~$ sudo rm /etc/asterisk/sip.conf

ekzorchik@srv-asterisk:~$ sudo nano /etc/asterisk/sip.conf

[general]

externaddr=172.33.33.2:5060

language=ru ;Локализация звуков

allowguest=no ;Разрешить/Запретить гостевые подключения

srvlookup=yes ;Принимать SIP-вызовы на основании доменных имен

limitopnpeers=yes ;Разрешить/Запретить лимит на кол-во одновременных разговоров

allowoverlap=no ;Разрешить/Запретить набор по одной цифре

useragent=Asterisk ekzorchik ;Значение поля useragent в SIP заголовке

[authentication]

[default-sip](!)

host=dynamic

type=friend

nat=no ;Политика работы через nat

deny=0.0.0.0/0.0.0.0 ;Сети из которых запрещено подключение

permit=0.0.0.0/0.0.0.0 ;Сети из которых разрешено подключение

qualify=yes ;Периодическая проверка доступности клиента

canreinvity=no ;Разрешить/Запретить потоки peer-to-peer в обход сервера

insecure=port,invite ;port (не требовать совпадение порта), invite (не требовать аутентификации)

call-limit=2 ;Лимит входящих вызовов

dtmfmode=auto ;Какую спецификацию использовать при передачи DTMF сигналов

context=phones ;Контекст обработки вызовов

disallow=all ;Запретить использование все кодеков, ниже разрешаем нужные

allow=alaw

allow=ulaw

[2002](default-sip)

callerid="Number 2002" <2002>

username=2002

secret=pbx2002

[2003](default-sip)

callerid="Number 2003" <2003>

username=2003

secret=pbx2003

ekzorchik@srv-asterisk:~$ sudo asterisk -rx "sip reload"

ekzorchik@srv-asterisk:~$ sudo cp /etc/asterisk/extensions.conf /etc/asterisk/extensions.conf.backup

ekzorchik@srv-asterisk:~$ sudo rm /etc/asterisk/extensions.conf

ekzorchik@srv-asterisk:~$ sudo nano /etc/asterisk/extensions.conf

[general]

[globals]

[default]

[handup-sip]

exten = > _X!,1,HangUp()

include => handup-sip

[local]

exten = _2XXX,1,Dial(SIP/${EXTEN})

[phones]

include = local

Шаг №1: Текущее время и дата:

ekzorchik@srv-asterisk:~$ date

Wed Nov 27 20:14:10 MSK 2019

Шаг №2: Т.к. я в конфигурационном файле sip.conf предопределил параметр language=ru, то мне нужно чтобы сервис asterisk использовал аудиофайлы русского языка в дополнении к тем которые я отмечал при установке Asterisk

ekzorchik@srv-asterisk:~$ sudo apt-get install -y asterisk-core-sounds-ru

Но можно и в диалплане использовать вот так

ekzorchik@srv-asterisk:~$ sudo nano /etc/asterisk/extensions.conf

[general]

[globals]

[default]

[handup-sip]

exten = > _X!,1,HangUp()

include => handup-sip

[russ]

exten => 2005,1,Answer()

exten => 2005,n,Set(CHANNEL(language)=ru)

exten => 2005,n,SayDigits(${CALLERID(num)})

exten => 2005,n,Wait(0.5)

exten => 2005,n,Hangup()

[local]

exten = _2XXX,1,Dial(SIP/${EXTEN})

[phones]

include = local

include = russ

ekzorchik@srv-asterisk:~$ sudo asterisk -rx "dialplan reload"

Dialplan reloaded.

Так почему-то при звонке на номер 2005 с номера 2003 я в лог получил:

== Using SIP RTP CoS mark 5

[Nov 28 21:09:50] WARNING[792][C-00000002]: chan_sip.c:6331 create_addr: Purely numeric hostname (2005), and not a peer--rejecting!

[Nov 28 21:09:50] WARNING[792][C-00000002]: app_dial.c:2578 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent)

== Everyone is busy/congested at this time (1:0/0/1)

ага я просто забыл указать в sip.conf данный номер, а то получается что я набираю несуществующий номер.

[2005](default-sip)

callerid="Number 2005" <2005>

username=2005

secret=pbx2005

ekzorchik@srv-asterisk:~$ sudo systemctl restart asterisk && sudo systemctl status asterisk | grep head -n5

Теперь при звонке на номер 2005 — я получаю проговаривание текущего номера с которого совершаю звонок, т.е. 2003. Работает. Двигаюсь дальше.

Так, как я оформляю данную заметку то аудио файлы я уже все откуда-то либо выкачал и они у меня лежат на моем OwnCloud 10.

  • https://own.ekzorchik.ru:38444/index.php/s/Ze6j48W4wpy96jP
  • https://172.35.35.6/index.php/s/Ze6j48W4wpy96jP

скачиваю sounds_ru.tar.gz, распаковываю и копирую содержимое в путь который предопределен у astvarlibdir (конфигурационного файла asterisk.conf) по умолчанию устанавливается путь: /var/lib/asterisk. Аудио файлы лежат в sounds поддиректории. В Asterisk(е) один и тот же audio файл может быть сохранен с разными расширениями которые определяют формат файла.

ekzorchik@srv-asterisk:~$ sudo apt-get install curl tree -y

ekzorchik@srv-asterisk:~$ curl -k -u "login:pass" https://172.35.35.6/index.php/s/Ze6j48W4wpy96jP/download -o sounds_ru.tar.gz

Но через WAN-адрес можно скачать без логина и пароля.

ekzorchik@srv-asterisk:~$ ls -lh sounds_ru.tar.gz

-rw-rw-r-- 1 ekzorchik ekzorchik 158M Nov 28 21:42 sounds_ru.tar.gz

ekzorchik@srv-asterisk:~$ tar zxf sounds_ru.tar.gz

tar: Removing leading `/' from member names

ekzorchik@srv-asterisk:~$

ekzorchik@srv-asterisk:~$ sudo cp var/lib/asterisk/sounds/ru/digits/* /var/lib/asterisk/sounds/ru/digits/

ekzorchik@srv-asterisk:~$ sudo chown -R asterisk:asterisk /var/lib/asterisk/sounds/ru/digits/

Отобразить количество каталогов — этот каталог содержит звуковые файлы которые я буду использовать с учетом скопированного выше:

ekzorchik@srv-asterisk:~$ tree -L 1 /var/lib/asterisk/sounds/ru/digits | grep directories

0 directories, 450 files

Шаг №3: Сервис точного времени буду базировать на использовании файла say.conf

ekzorchik@srv-asterisk:~$ sudo mv /etc/asterisk/say.conf /etc/asterisk/say.conf.backup

ekzorchik@srv-asterisk:~ $ sudo nano /etc/asterisk/say.conf

[ru-base](!)

_[n]um:0X => num:${SAY:1}

_[n]um:X => digits/${SAY}

_[n]um:[1-2]f => digits/${SAY:0:1}f

_[n]um:[3-9]f => digits/${SAY:0:1}

;Tens

_[n]um:1X => digits/${SAY:0:2}

_[n]um:1Xf => digits/${SAY:0:2}

_[n]um:[2-9]0 => digits/${SAY:0:2}

_[n]um:[2-9]0f => digits/${SAY:0:2}

_[n]um:[2-9][1-2] => digits/${SAY:0:1}0, num:${SAY:1}

_[n]um:[2-9][1-2]f => digits/${SAY:0:1}0, num:${SAY:1}

_[n]um:[2-9][3-9] => digits/${SAY:0:1}0, num:${SAY:1}

_[n]um:[2-9][3-9]f => digits/${SAY:0:1}0, num:${SAY:1}

;Hundreds

_[n]um:0XX => num:${SAY:1}

_[n]um:0XXf => num:${SAY:1}

_[n]um:[1-9]00 => digits/${SAY:0:1}00

_[n]um:[1-9]00f => digits/${SAY:0:1}00

_[n]um:XXX => num:${SAY:0:1}00, num:${SAY:1}

_[n]um:XXXf => num:${SAY:0:1}00, num:${SAY:1}

;enumeration(числительные)

_e[n]um:X => digits/h-${SAY}

_e[n]um:X[n] => digits/h-${SAY}

_e[n]um:0X => enum:${SAY:1}

_e[n]um:0X[n] => enum:${SAY:1}

_e[n]um:1X => digits/h-${SAY}

_e[n]um:1X[n] => digits/h-${SAY}

_e[n]um:[2-9]0 => digits/h-${SAY}

_e[n]um:[2-9]0[n] => digits/h-${SAY}

_e[n]um:[2-9][1-9] => num:${SAY:0:1}0, digits/h-${SAY:1}

_e[n]um:[2-9][1-9][n] => num:${SAY:0:1}0, digits/h-${SAY:1}

_e[n]um:[1-9]00 => digits/h-${SAY}

_e[n]um:[1-9]00[n] => digits/h-${SAY}

_e[n]um:[1-9]XX => num:${SAY:0:1}00, enum:${SAY:1}

_e[n]um:[1-9]XX[n] => num:${SAY:0:1}00, enum:${SAY:1}

[ru](ru-base)

_chas:0 => num:${SAY}, digits/hours

_chas:1 => digits/${SAY}, digits/hour

_chas:[2-4] => num:${SAY}, digits/hours-a

_chas:[5-9] => num:${SAY}, digits/hours

_chas:0X => chas:${SAY:1}

_chas:1X => num:${SAY}, digits/hours

_chas:20 => num:${SAY}, digits/hours

_chas:2[1-4] => num:${SAY:0:1}0, chas:${SAY:1}

_mi[n]uta:0 => num:${SAY}, digits/minutes

_mi[n]uta:1 => digits/1f, digits/minute

_mi[n]uta:2 => digits/2f, digits/minutes-i

_mi[n]uta:[3-4] => num:${SAY}, digits/minutes-i

_mi[n]uta:[5-9] => num:${SAY}, digits/minutes

_mi[n]uta:0X => minuta:${SAY:1}

_mi[n]uta:1X => num:${SAY}, digits/minutes

_mi[n]uta:[2-5]0 => num:${SAY}, digits/minutes

_mi[n]uta:[2-5][1-9] => num:${SAY:0:1}0, minuta:${SAY:1}

_seku[n]da:0 => num:${SAY}, seconds

_seku[n]da:[5-9] => num:${SAY}, seconds

_seku[n]da:0X => sekunda:${SAY:1}

_seku[n]da:1X => num:${SAY}, seconds

_seku[n]da:[2-5]0 => num:${SAY}, seconds

_dayofweek:[0-6] => digits/day-${SAY}

_dayofmo[n]th:X => enum:${SAY}n

_dayofmo[n]th:XX => enum:${SAY}n

_mo[n]th:X => digits/mon-$[${SAY} - 1]

_mo[n]th:XX => digits/mon-$[${SAY} - 1]

После не забываем сохранить внесенные изменения.

Применяем настройки:

ekzorchik@srv-asterisk:~$ sudo asterisk -rx "module reload app_playback.so"

Module 'app_playback.so' reloaded successfully.

Контекст [ru-base] в say.conf имеет завершающий (!) восклицательный знак в скобках означает, что это шаблон, аналогия когда в sip.conf описываем дефолтные
настройки для аккаунтов.

Попробуем разобрать одно правило. Первое, на что стоит обратить внимание, это символы X Z N. Они интерпретируются asterisk`ом как специальные и если эти литеры фигурируют в названии правила чтения, их следует взять в квадратные скобки, например mo[n]th.

_mo[n]th:XX => digits/mon-$[${SAY} — 1]

Синтаксис достаточно прост и правило совпадает, если входные данные XX — две любые цифры. Проигрываем файл digits/mon-(XX-1), где (XX-1) это арифметическая операция. При X=02 (да, «переваривает» даже такие цифры, что нам очень поможет), 02-1=1, digits/mon-1: «Февраля».

Отдельно стоит упомянуть секунды. Во-первых, в записанных фразах есть только единственная запись: «секунд». Это значит, что на вход этой функции должны приходить округленные данные, например 0, 10, 20, и так далее.

ekzorchik@srv-asterisk:~$ sudo nano /etc/asterisk/extensions.conf

[general]

[globals]

[default]

[handup-sip]

exten => _X!,1,HangUp()

include => handup-sip

[info]

exten = 1000,1,Answer()

same = n,Goto(informer_1000,s,1)

[informer_1000]

exten => s,1,Set(FreezeEPOCH=$[${EPOCH} + 15]) ; Добавляем 15 секунд к unixtime.

same => n,Set(TimeNow=${STRFTIME(${FreezeEPOCH},,%Y%m%d%H%M.%S-%w-%j)})

same => n,Playback(silence/1&at-tone-time-exactly) ;Тишина 1 секунда + Текущее время

same => n,Playback(chas:${TimeNow:8:2},say) ; десять + часов

same => n,Playback(minuta:${TimeNow:10:2},say) ; сорок + одна + минута

same => n,Playback(sekunda:${TimeNow:13:1}0,say) ; двадцать + секунд

same => n,Playback(silence/1&digits/today) ; тишина 1 секунда + сегодня

same => n,Playback(dayofweek:${TimeNow:16:1},say) ; четверг

same => n,Playback(dayofmonth:${TimeNow:6:2},say) ; шестнадцатое

same => n,Playback(month:${TimeNow:4:2},say) ; октября

same => n,Playback(silence/1&beep) ; тишина 1 секунда + короткий гудок

same => n,Hangup()

[russ]

exten => 2005,1,Answer()

exten => 2005,n,Set(CHANNEL(language)=ru)

exten => 2005,n,SayDigits(${CALLERID(num)})

exten => 2005,n,Wait(0.5)

exten => 2005,n,Hangup()

[local] exten = _2XXX,1,Dial(SIP/${EXTEN})

[phones]

include = local

include = russ

include = info

Применяем настройки:

ekzorchik@srv-asterisk:~$ sudo asterisk -rx "dialplan reload"

Dialplan reloaded.

ekzorchik@srv-asterisk:~$ sudo systemctl restart asterisk && sudo systemctl status
asterisk | head -n5

Проверяю, набираю на телефоне номер 1000, а тем временем в открытой консоли:

ekzorchik@srv-asterisk:~$ sudo asterisk -rvvv

и ошибок нет. Была произнесена фраза: В момент звукового сигнала точное
время будет 22 часа 10 минут 30 секунд Сегодня 28 ноября

Задача: Нужно доработать как изменить формуляр чтобы еще и произносило год с этому: 21 час 55 минут 40 секунд. Сегодня Среда 27 ноября

ekzorchik@srv-asterisk:~$ sudo nano /etc/asterisk/extensions.conf

same => n,Playback(year:${TimeNow:0:4},say); год

но не произносит, а лишь пишет ниже (лог)

--
Executing [s@informer_1000:10] Playback("SIP/2003-0000000b", "month:11,say") in new stack

--
<SIP/2003-0000000b> Playing 'digits/mon-10.alaw' (language 'ru')

--
Executing [s@informer_1000:11] Playback("SIP/2003-0000000b", "year:2019,say") in new stack

Добился чтобы произносился год — пока будет просто цифрами:

ekzorchik@srv-asterisk:~$ sudo nano /etc/asterisk/extensions.conf

[general]

[globals]

[default]

[handup-sip]

exten = > _X!,1,HangUp()

include => handup-sip

[local]

exten = _2XXX,1,Dial(SIP/${EXTEN})

[info]

exten = 1000,1,Answer()

same = n,Goto(informer_1000,s,1)

[informer_1000]

exten => s,1,Set(FreezeEPOCH=$[${EPOCH} + 15]) ; Добавляем 15 секунд к unixtime.

same => n,Set(TimeNow=${STRFTIME(${FreezeEPOCH},,%Y%m%d%H%M.%S-%w-%j)})

same => n,Playback(silence/1&at-tone-time-exactly) ;Тишина 1 секунда + Текущее время

same => n,Playback(chas:${TimeNow:8:2},say) ; десять + часов

same => n,Playback(minuta:${TimeNow:10:2},say) ; сорок + одна + минута

same => n,Playback(sekunda:${TimeNow:13:1}0,say) ; двадцать + секунд

same => n,Playback(silence/1&digits/today) ; тишина 1 секунда + сегодня

same => n,Playback(dayofweek:${TimeNow:16:1},say) ; четверг

same => n,Playback(dayofmonth:${TimeNow:6:2},say) ; шестнадцатое

same => n,Playback(month:${TimeNow:4:2},say) ; октября

same => n,Playback(num:${TimeNow:0:2},say); год

same => n,Playback(num:${TimeNow:2:2},say); год

same => n,Playback(silence/1&beep) ; тишина 1 секунда + короткий гудок

same => n,Hangup()

[phones]

include = local

include = info

После не забываем сохранить внесенные изменения:

%Y%m%d%H%M.%S-%w-%j

  • %Y -> год
  • %m -> месяц (01..12)
  • %d -> день в месяца (01,02 и т.д)
  • %H -> часы (00..23)
  • %M -> минуты (00..59)
  • %S -> секунды (00..60)
  • %w -> дать недели (0..6, где 0 это воскресенье)
  • %j -> день в годы (001..366)

same => n,Set(FreezeEPOCH=$[${EPOCH} + 15])

FreezeEPOCH → переменная

В контексте [informer_100] я использую переменную FreezeEPOCH к оторой добавляю 15 секунд к unixtime. Это нужно дабы компенсировать время потраченной на проигрывание файла с учетом выполнения. Далее формируется формат даты в переменной TimeNow. Она содержит данные в виде: 2019(%Y).10(%m).16(%d).00(%H).43(%M).34(%S)-4(%w)-289(%j). При чтении мы выдергиваем из «массива» необходимые числа. Они всегда на своих местах и извлечение не составит труда.

Полный синтаксис переменной ${AnyVariable:x:y}, где x — начальное положение, а y — количество цифр, которое должно быть возвращено. Пусть задана строка:
201410160043.34-4-289
Используя конструкцию ${AnyVariable:x:y}, можно извлечь следующие данные:
${AnyVariable:0:4}

0 — пропустить ноль символов слева

4 — взять слева четыре символа

${AnyVariable:0:4} — будет возвращена строка 2014. Пропустить ноль символов слева и взять четыре символа.

${AnyVariable:4:8} — будет возвращена строка 10160043. Пропустить 4 символа слева и взять восемь символов.

${AnyVariable:-3:3} — строка будет начинаться с третьего символа, считая с конца и включает три символа, что даст 289.

${AnyVariable:2} — если количество цифр, которое должно быть возвращено, не задано, будет возвращена вся оставшаяся строка, получим 1410160043.34-4-289.

Исходя из say.conf, секунды у нас должны округляться. Из двухзначного формата секунд мы выбираем первую цифру и добавляем к ней ноль: ${TimeNow:13:1}0

ekzorchik@srv-asterisk:~$ sudo asterisk -rx "dialplan reload"

Dialplan reloaded.

ekzorchik@srv-asterisk:~$ sudo asterisk -rx "core reload" && sudo asterisk -rvvvv

в логах идет проигрывание *.alaw файлов по умолчанию, если файла нет то любой другой с таким же именованием.

  • silence/1.alaw
  • at-tone-time-exactly.alaw
  • digits/20.alaw
  • digits/2.alaw
  • digits/hours-a.slin
  • digits/minutes-i.slin
  • digits/today.alaw
  • digits/day-4.alaw
  • digits/h-8n.slin
  • silence/1.alaw
  • beep.alaw

Произносит все: 22 час 55 минут 40 секунд. Сегодня Среда 27 ноября 2019 кроме
слова год. А звукового файла «год» в аудиофайлах asterisk кажись нет. Нужно
наверное самим записать звуковой файл «год».

Для этого на своем рабочем месте — Ubuntu 18.04 Desktop amd64 ноутбук Lenovo E555 устанавливаю приложение Audacity:

ekzorchik@navy:~$ sudo apt-get install -y audacity

Т.к. Asterisk воспроизводит аудио файлы в формате mono, то после того как запустили
аудиоредактор:

  • Дорожки - Создать новую - Моно дорожку
  • Нажимаю на кнопку с красным кружком и произношу в микрофон "год"

Создаю аудио файл для Asterisk 16 через Audacity

  • Затем нажимаю на иконку квадрата дабы остановить запись.
  • Теперь необходимо поменять частоту звучания с 44100 Гц в 8000 Гц. Выбираем: Правка — Параметры:
  • Выбираем частоту дискретизации по умолчанию — 8000 Гц.
  • Теперь экспортирую данный файл. Делается это так: Файл — Export - Экспортировать как WAV
ekzorchik@navy:~$ file Downloads/year.wav

Downloads/year.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono
8000 Hz

Теперь копирую данный аудио файл в дефолтный каталог аудио файлов Asterisk и добавляю строчку в extensions.conf на предмет его воспроизведения в плане обработки:

ekzorchik@srv-asterisk:~$ sudo scp ekzorchik@172.33.33.16:/home/ekzorchik/Downloads/year.wav
/var/lib/asterisk/sounds/ru/digits/year.wav

ekzorchik@172.33.33.16's password:

year.wav 100% 24KB 6.2MB/s 00:00

ekzorchik@srv-asterisk:~$

ekzorchik@srv-asterisk:~$ sudo chown asterisk:asterisk
/var/lib/asterisk/sounds/ru/digits/year.wav

ekzorchik@srv-asterisk:~$ sudo nano /etc/asterisk/extensions.conf

[informer_1000]

exten => s,1,Set(FreezeEPOCH=$[${EPOCH} + 15]) ; Добавляем 15
секунд к unixtime.

same => n,Set(TimeNow=${STRFTIME(${FreezeEPOCH},,%Y%m%d%H%M.%S-%w-%j)})

same => n,Playback(silence/1&at-tone-time-exactly) ;Тишина 1
секунда + Текущее$

same => n,Playback(chas:${TimeNow:8:2},say) ; десять + часов

same => n,Playback(minuta:${TimeNow:10:2},say) ; сорок + одна
+ минута

same => n,Playback(sekunda:${TimeNow:13:1}0,say) ; двадцать +
секунд

same => n,Playback(silence/1&digits/today) ; тишина 1 секунда
+ сегодня

same => n,Playback(dayofweek:${TimeNow:16:1},say) ; четверг

same => n,Playback(dayofmonth:${TimeNow:6:2},say) ; шестнадцатое

same => n,Playback(month:${TimeNow:4:2},say) ; октября

same => n,Playback(num:${TimeNow:0:2},say) ; год

same => n,Playback(num:${TimeNow:2:2},say) ; год

same => n,Playback(digits/year) ; год

same => n,Playback(silence/1&beep) ; тишина 1 секунда
+ короткий гудок

same => n,Hangup()

ekzorchik@srv-asterisk:~$ sudo asterisk -rx "dialplan reload"

Dialplan reloaded.

ekzorchik@srv-asterisk:~$

Пусть и грубо но все же по хендмайду.

Работает. Пусть данная заметка будет моей первой отправной точкой к последующим задача которые я буду решать для своего «Умного дома» который я строю на базе Zabbix Server 4.4 on Ubuntu 18.04 Server amd64. Все выше опробовано и работает у меня. На этом я прощаюсь, с уважением автор блога Олло Александр
aka ekzorchik.