Залиште свою АТС
signal2sip не замінює жодної частини вашої телефонії - він реєструється на вашій наявній Asterisk, FreePBX чи будь-якій іншій SIP-сумісній АТС як звичайний SIP-екстеншн, так само, як стаціонарний телефон чи софтфон.
Як він підключається
Кожен акаунт Signal, яким керує signal2sip, отримує власний виділений SIP-транспорт і реєстрацію, налаштовані окремо для кожного акаунта:
sip_host/sip_extension/sip_password- звичайна SIP-реєстрація на вашій АТС.sip_transport=udpабоsip_transport=tls(SIPS) - простий UDP чи захищений TLS-транспорт сигналізації, на ваш вибір для кожного акаунта.sip_srtp=disabled|optional|mandatory- чи захищено саме медіа (RTP) через SDES-SRTP поверх транспорту сигналізації.
АТС не потрібно знати, що signal2sip - це не звичайний телефон: ваш наявний діалплан, вхідні маршрути та нумерація екстеншнів продовжують працювати без змін.
Обидва напрямки
- Вхідний дзвінок Signal, з’єднаний з АТС:
sip_bridge_destination(конкретний екстеншн) абоsip_bridge_did(DID у стилі вхідного маршруту, коли логіка FreePBX - маршрутизація/IVR/чергу дзвінків - сама вирішує реальне призначення) - див. Двосторонній зв’язок. - Вихідний дзвінок з АТС, з’єднаний із Signal: наберіть номер через екстеншн signal2sip, і він розмістить справжній вихідний дзвінок у Signal, розпізнавши набраний номер через власний сервіс пошуку контактів Signal.
Кодек: ваш ендпоінт АТС повинен дозволяти L16/48000 (slin48)
m=audio 0, АТС відповідає 488 Not Acceptable Here).Внутрішній аудіоміст signal2sip передає сире, нестиснене аудіо між RingRTC і PJSIP без жодного кроку транскодування - навмисно, щоб уникнути витрат CPU і втрати якості від кодування/декодування кожного пакета. Це працює, лише якщо PJSIP узгоджує точно той формат, який міст уже використовує внутрішньо, тож демон свідомо пропонує єдиний кодек для кожного SIP-дзвінка: L16/48000/1 (16-бітний лінійний PCM, 48кГц, моно) - усі інші кодеки, відомі PJSIP (G.711, G.722, Opus, GSM тощо), явно вимкнені, а не просто мають нижчий пріоритет.
Що це означає для конфігурації вашої АТС: ендпоінт/транк, на якому
реєструється signal2sip, повинен дозволяти цей кодек, інакше дзвінок
взагалі не отримає медіа. В Asterisk/FreePBX (chan_pjsip) цей кодек
називається slin48 - переконайтеся, що він є в списку дозволених
кодеків ендпоінта, наприклад:
[signal2sip-trunk]
allow=!all,slin48Якщо сигналізація SIP виглядає нормально (реєстрація проходить, INVITE
надсилається), але аудіо так і не з’являється, а АТС відповідає 488 -
першим ділом перевірте саме цю невідповідність кодеків.
Caller ID на ваших телефонах
Коли надходить дзвінок Signal і з’єднується з АТС, signal2sip додає справжню Signal-ідентичність абонента до вихідного INVITE у вигляді кастомних SIP-заголовків:
X-Signal-UUID- присутній завжди, ідентифікатор акаунта Signal абонента.X-Signal-Phone/X-Signal-Name- за можливості, заповнюється, коли номер телефону/ім’я цього контакту вже відомі локально (синхронізовані з контактів Signal) - гарантії для кожного абонента немає.
FreePBX/Asterisk за замовчуванням не читають кастомні SIP-заголовки,
тож у dialplan потрібен явний крок, що їх зчитує і перетворює на
справжній Caller ID. Мінімальний приклад для FreePBX, у кастомному
контексті через PJSIP_HEADER:
[from-signal2sip-headers]
exten => _X.,1,Set(CALLERID(name)=${IF($["${PJSIP_HEADER(read,X-Signal-Name)}"!=""]?${PJSIP_HEADER(read,X-Signal-Name)}:Signal#${PJSIP_HEADER(read,X-Signal-UUID)})})
same => n,Set(CALLERID(num)=${IF($["${PJSIP_HEADER(read,X-Signal-Phone)}"!=""]?${PJSIP_HEADER(read,X-Signal-Phone)}:${PJSIP_HEADER(read,X-Signal-UUID)})})
same => n,Dial(PJSIP/${EXTEN},30)Направте відповідний вхідний маршрут (саме конкретний DID, не catch-all - catch-all-маршрути обробляються останніми і ніколи це не побачать) на цей контекст замість прямого набору. Після налаштування дзвінок Signal, з’єднаний з АТС, показує реальне ім’я/номер абонента на ваших настільних телефонах - так само, як будь-який інший транк.
Покроково: налаштування повністю через веб-інтерфейс FreePBX
Якщо ви ніколи раніше не редагували dialplan Asterisk вручну, мінімальний приклад вище може бути незрозуміло, як саме застосувати - ось той самий результат, зроблений від початку до кінця через веб-інтерфейс, із трохи надійнішою версією діалплану, яка на практиці тримається краще за мінімальну.
Додайте контекст
Власний модуль FreePBX Config Edit (Admin -> Config Edit) дозволяє
додати це, взагалі не відкриваючи SSH-термінал: оберіть
extensions_custom.conf, вставте блок нижче і збережіть. Він робить те
саме, що й мінімальний приклад вище, плюс три реальні покращення, які
варто мати, раз вже вставляєте це на робочу АТС:
[from-signal2sip-headers]
exten => _.,1,NoOp(Incoming Signal call via signal2sip)
same => n,Set(SIG_UUID=${PJSIP_HEADER(read,X-Signal-UUID)})
same => n,Set(SIG_PHONE=${PJSIP_HEADER(read,X-Signal-Phone)})
same => n,Set(SIG_NAME=${PJSIP_HEADER(read,X-Signal-Name)})
same => n,Set(CALLERID(name)=${IF($["${SIG_NAME}"!=""]?${SIG_NAME}:Signal#${SIG_UUID})})
same => n,Set(CALLERID(num)=${IF($["${SIG_PHONE}"!=""]?${SIG_PHONE}:${SIG_UUID})})
same => n,Dial(PJSIP/106,30)
same => n,Hangup()_.замість_X.- у цей контекст заходять через Custom Destination (нижче), а не через звичайний збіг номера, тож шаблон екстеншна має приймати все, що передасть FreePBX, а не лише цифри.- Кожен заголовок зчитується один раз, у
SIG_UUID/SIG_PHONE/SIG_NAME, замість викликуPJSIP_HEADER(read,...)прямо всерединіIF(...)- Asterisk підставляє кожен${...}у рядку ще до того, як обчислитьIF(), тож вбудований варіант непомітно зчитує кожен заголовок двічі на рядок (раз для умови, раз для гілки) незалежно від того, яка гілка переможе. Тут це нешкідливо, але варто знати, перш ніж покладатися на такий вбудований варіант деінде у вашому діалплані. - Замініть
106на реальний екстеншн, на якому мають дзвонити Signal-виклики у вас. На відміну від мінімального прикладу, тут номер не набирається динамічно через${EXTEN}- на цьому етапі виконання${EXTEN}- це те, що передав Custom Destination, а не обов’язково реальна назва PJSIP-екстеншна. - Завершальний
Hangup()робить завершення дзвінка явним, якщоDial()повернувся без відповіді (зайнято/немає відповіді/тайм-аут), замість покладатися на неявну обробку кінця діалплану самим Asterisk.
Застосуйте конфігурацію (звичайна червона панель) після збереження.
Зареєструйте як Custom Destination
Admin -> Custom Destinations -> Add Destination. У полі Target
вкажіть from-signal2sip-headers,${EXTEN},1 (контекст, який ви щойно
додали, пріоритет 1), і дайте опис, який потім впізнаєте, наприклад
“SIGNAL2SIP Caller Info”:

Збережіть і застосуйте конфігурацію.
Направте вхідний маршрут на нього
Connectivity -> Inbound Routes, відкрийте саме той DID, на який
sip_bridge_did signal2sip надсилає дзвінки (не catch-all -
catch-all-маршрути обробляються останніми і ніколи це не побачать), і
встановіть його Destination на щойно створений Custom Destination,
замість прямого набору екстеншна.
Жива конфігурація, без перезапуску
Кожне з налаштувань конкретного акаунта живе у власній базі даних
демона, а не в статичному файлі конфігурації - у статичному файлі
/etc/signal2sip/signal2sip.conf, у єдиній секції [global],
залишається лише невелика кількість загальних для процесу налаштувань
(шлях/ключ бази даних, інтервали вотчдогів та опитування). Змінюйте
налаштування акаунта через signal2sip-gendb <name> config set <field> <value> або через термінальний інтерфейс, і демон підхопить зміну
наживо (SIGHUP або періодичний опитувальний цикл) без перезапуску. Див.
Керування наживо.
Цей рядок акаунта в базі даних не зачіпає м’який deactivate (зворотна,
суто серверна зміна прапорця - усі локальні дані залишаються на місці,
reactivate скасовує це) - видаляє його лише unlink (стирає локальний
рядок цього акаунта, реальний акаунт Signal не чіпає) або успішний
delete-account (який також знищує акаунт на самих серверах Signal).