Иногда случается так, что надо отправлять откуда-нибудь SMS-сообщения. При этом неважно — откуда именно. Речь может идти о домашней системе, объединяющей кучу устройств (эти системы называют «homelab»), из которой надо отправлять уведомления. Это может быть система сигнализации, информирующая своего владельца о разных событиях, требующих его внимания. Возможно это — программный комплекс, которому нужно подтверждать правильность телефонных номеров, вводимых его пользователями. Во всех этих случаях, да и во многих других, SMS-сообщения были и остаются наилучшим способом передачи неких уведомлений по инициативе отправителя.
Но в большинстве вышеописанных сценариев использования SMS нельзя положиться на внешние SMS-шлюзы или API, так как система отправки уведомлений должна работать даже тогда, когда пропадает доступ в интернет. Особенно это важно для охранных систем, которые должны функционировать независимо от внешних источников питания.
Очевидное решение всех этих проблем заключается в поддержке собственного SMS-шлюза.
Результат этого проекта: устройство, поддерживающее полнофункциональный REST API, способное отправлять и получать сообщения
Как, с минимальными затратами, самостоятельно сделать такой шлюз?
Если в двух словах описать решение этой задачи, то оно заключается в использовании пакета gammu-smsd
и кода API с GitHub. А подробный ответ на этот вопрос приведён ниже.
Компоненты
▍Raspberry Pi
Одноплатные компьютеры Raspberry Pi
На самом деле — неважно, какой именно одноплатник Raspberry Pi выбрать. Можно даже использовать самый первый — тот, что появился в 2007 году.
Если говорить об операционной системе, то тут подойдёт и Raspbian, и Alpine Linux (да и, в общем-то, любая ОС, в которой, без особых сложностей, можно установить необходимые нам пакеты). Я выбрал Alpine из-за того, что она работает с RAM-диска, то есть, внезапное отключение питания не повредит данные на SD-карте. Но я тут приведу и инструкции по настройке Raspbian.
▍3G/4G USB-модем
<img src=«lh6.googleusercontent.com/5n85NqWuLeMp_Sfr4EaZOSsLJke_EYSKXrM4uo5CtI-akICGMvfX1Wj3TePfi2PZujsm1EUis7whpqu9afqqqa3wI70HgbmSp1JBp-7bNaOSOUAPpS_LeQ1PqeIaUQzEKQyLtz8j» «>USB-модем Huawei E303
В моём проекте используется USB-модем Huawei E303. Не все USB-модемы подойдут для подобных проектов, но практически все модемы Huawei этой серии недороги и их легко достать. Если у вас уже есть USB-модем, можно заранее поискать в интернете сведения о том, работает ли он с Raspberry Pi, чтобы не оказалось так, что он вам не подойдёт.
Понятно, что понадобится ещё и SIM-карта с тарифным планом, поддерживающим работу с SMS-сообщениями.
Шаг 1: подготовка Raspberry Pi
Я исхожу из предположения о том, что у вас уже имеется работающий одноплатный компьютер Raspberry Pi, на котором вы можете выполнять команды — либо по ssh, либо подключив к нему мышь, клавиатуру и монитор.
Нам понадобятся некоторые пакеты.
Вот команда для их установки в Apline Linux:
apk add gammu gammu-smsd php php-json usb-modeswitch usbutils git
Вот — команда для Raspbian:
apt install gammu gammu-smsd php php-json usb-modeswitch git
Большинство USB-модемов при подключении к компьютеру, по умолчанию, работают в режиме устройства хранения данных (storage mode), а нам нужен режим модема (modem mode). Для того чтобы понять, в каком именно режиме работает ваш модем — подключите его к настольному компьютеру. Если система монтирует его в виде USB-диска (обычно содержащего установщики разных программ и драйверов) — вам надо переключить его в режим модема.
Переключение режима устройства в различных модемах выполняется немного по-разному (тут, опять же — Google вам в помощь). В моём случае всё чудесным образом переключилось с помощью такой команды:
usb_modeswitch -W -v 12d1 -p 14fe -K -P 14ac -M "55534243000000000000000000000011060000000000000000000000000000"
Команда lsusb
возвращает Bus 001 Device 005: ID 12d1:1c05 HUAWEI HUAWEI Mobile
. 12d1
— это код поставщика для Huawei, а строка, идущая за этим кодом (в моём случае — 1c05
) — это ID продукта. Мы используем данные, полученные с помощью lsusb
, при вызове usb_modeswitch
.
Вы, чтобы изменить режим работы своего модема, можете обратиться к Arch Wiki или к Ubuntuusers Wiki. Там должно быть всё необходимое.
Если система, после подключения модема, создаст /dev/ttyUSB0
, это значит, что всё сделано правильно.
pi:~# ls -al /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0 Dec 3 12:10 /dev/ttyUSB0
После этого аппаратная часть нашего проекта — Raspberry Pi и модем — готова к дальнейшей работе.
Шаг 2: установка Gammu
Что такое Gammu? Процитирую разработчиков проекта:
Утилита командной строки Gammu — это средство, дающее доступ к широкому набору телефонных возможностей.
В этой утилите уже реализованы сложные механизмы, ответственные за разбор сообщений и за их отправку по мобильным сетям. А нам нужно лишь сообщить утилите о том, как ей общаться с модемом, который мы настроили на предыдущем шаге.
Для того чтобы узнать о том, видит ли утилита модем, можно выполнить команду gammu identify
. Если всё нормально — в ответ вы получите примерно следующее:
vpnpi:~# gammu identify
Device : /dev/ttyUSB0
Manufacturer : Huawei
Model : E303 (E303)
Firmware : 21.157.01.00.199
IMEI : 860000000000619
SIM IMSI : 230000000000006
Пока всё нормально. Создадим теперь конфигурационный файл со следующим содержимым и сохраним его в папке /etc/gammurc
:
[gammu]
device = /dev/ttyUSB0
name = Bob
connection = at
logfile = /var/log/gammu.log
[smsd]
service = files logfile = syslog #PIN = 1234 # Увеличьте значение для получения отладочной информации debuglevel = 0 # Пути, по которым хранятся сообщения inboxpath = /var/spool/gammu/inbox/ outboxpath = /var/spool/gammu/outbox/ sentsmspath = /var/spool/gammu/sent/ errorsmspath = /var/spool/gammu/error/
Имя (поле Name
) может быть любым. Я выбрал Bob
— так зовут «абонента», от имени которого отправляются сообщения.
Если для работы с вашей SIM-картой нужен PIN-код — его можно задать в соответствующем поле раздела [smsd]
.
▍Быстрая проверка возможности отправки SMS
Для того чтобы проверить, что всё, что мы до сих пор настраивали, правильно работает, и то, что мы можем отправлять SMS-сообщения — достаточно выполнить такую команду:
echo "some message" | gammu --sendsms TEXT 0664xxxxxxx
Ясно, что вам надо будет, как минимум, подставить сюда свой номер телефона.
Попробуем:
vpnpi:~# echo "Hello from your Pi" | gammu --sendsms TEXT 0664xxxxxxx
If you want break, press Ctrl+C…
Sending SMS 1/1…waiting for network answer..OK, message reference=22
Заглянем в телефон.
SMS успешно получено
Работает! На телефон пришло сообщение.
На этом можно и остановиться, автоматизировав отправку сообщений средствами командной строки. Но мне хотелось бы отправлять и получать сообщения с помощью API, к которому можно обращаться с любого устройства, находящегося в моей домашней сети, поэтому в описании этого проекта присутствует ещё два шага.
Шаг 3: подготовка к созданию API для отправки и получения SMS-сообщений
Мы, для отправки сообщений, использовали команду gammu
. Но получение сообщений — операция более сложная, её непросто автоматизировать. Правда — её можно было бы назвать сложной в том случае, если бы создатели Gammu не написали бы ещё и утилиту gammu-smsd
.
▍Утилита gammu-smsd
На вопрос о том, что такое gammu-smsd
, как и прежде, лучше всего способны ответить разработчики проекта:
Gammu SMS Daemon — это программа, которая периодически сканирует SMS-модем на предмет полученных сообщений, сохраняет их в заданном месте и, кроме того, отправляет сообщения, которые находятся в хранилище и ждут отправки. Это — программа, которая отлично подходит для автоматизации управления большими объёмами принятых или отправленных сообщений.
Получается, что gammu-smsd
— это демон, который ожидает появления новых входящих или исходящих SMS-сообщений.
Так как в вышеприведённом конфигурационном файле для Gammu уже содержится всё, что нужно для gammu-smsd
, нам осталось лишь создать папки, где gammu
будет хранить данные:
mkdir -p /var/spool/gammu/inbox/
mkdir -p /var/spool/gammu/outbox/
mkdir -p /var/spool/gammu/sent/
mkdir -p /var/spool/gammu/error/
▍Отправка сообщений
Запустим gammu-smsd
в режиме демона (то есть — позволим программе выполняться в фоновом режиме) и скажем ей о том, где находится созданный нами ранее конфигурационный файл:
gammu-smsd -d -c /etc/gammurc
Так как теперь у нас имеется демон, который общается с модемом, пользоваться ранее рассмотренным способом отправки сообщений с помощью gammu
мы уже не сможем. Правда, мы, всё же, можем их отправлять самостоятельно, но для этого нам понадобится команда gammu-smsd-inject
, созданная для использования совместно с gammu-smsd
. Теперь отправка сообщений выглядит как постановка их в локальную очередь, а демон будет брать сообщения из этой очереди и отправлять их.
Пример команды для отправки сообщения будет выглядеть так:
gammu-smsd-inject TEXT 0664xxxxxxx -unicode -text "hello world from the daemon!"
Заглянем в телефон.
Привет от демона получен
Мы, как и прежде, получили сообщение. В этот раз его доставка может занять на несколько секунд дольше, чем прежде. Такое ощущение, что демон проверяет очередь исходящих сообщений с некоторой периодичностью.
pi:~# gammu-smsd-inject TEXT 0664xxxxxxx -unicode -text "hello world from the daemon!"
gammu-smsd-inject[2964]: Warning: No PIN code in /etc/gammu-smsdrc file
gammu-smsd-inject[2964]: Created outbox message OUTC20211203_193330_00_0664xxxxxxx_sms0.smsbackup
Written message with ID /var/spool/gammu/outbox/OUTC20211203_193330_00_0664xxxxxxx_sms0.smsbackup
▍Получение сообщений
Пришло время отправить SMS нашей системе и выяснить, дойдёт ли оно до получателя. Для этого достаточно ответить на одно из полученных от системы сообщений и посмотреть, появится ли оно в виде файла в папке /var/spool/gammu/inbox/
.
Отправляем сообщение, ждём несколько секунд и заглядываем в папку.
pi:~# ls /var/spool/gammu/inbox/
IN20211203_194458_00_+43664xxxxxxx_00.txt
pi:~# cat /var/spool/gammu/inbox/IN20211203_194458_00_+43664xxxxxxx_00.txt
Hello also from the outside world!
Каждое из полученных сообщений сохраняется в отдельном файле. Можно догадаться, что в имени файла вида IN20211203_194458_00_+43664xxxxxxx_00.txt
закодированы дата, время, телефон отправителя и номер части сообщения (для SMS, длина которых превышает 140 символов, так как они разбиваются на части).
Шаг 4: API для работы с сообщениями
Нам нужен простой механизм для отправки и получения сообщений с использованием API, такой, для применения которого не нужно устанавливать множество плагинов и пакетов. Я написал пару весьма компактных PHP-скриптов, которые заточены именно под эту задачу. Найти их можно здесь.
Для того чтобы они функционировали, нужен работающий демон gammu-smsd
.
В директории, где лежат соответствующие .php-файлы (send.php
и get.php
) нужно выполнить команду php -S 0.0.0.0:8080
. Благодаря этому обратиться к ним из сети сможет любая нуждающаяся в них сущность.
▍Отправка SMS с помощью API
Теперь всё делается до крайности просто и понятно. Для отправки сообщения достаточно обратиться по адресу такого вида:
http://ip.of.your.pi/send.php?phone=0664xxxxxxx&text=Testmessage
В ответ придёт JSON-объект, в котором будут сведения о том, удалась ли отправка сообщения (status:ok
) или нет (status:error
).
{
"status": "ok",
"log": "2021-12-04 15:43:39\ngammu-smsd-inject TEXT 0664xxxxxxx -unicode -text 'Testmessage'\ngammu-smsd-inject[2669]: Warning: No PIN code in /etc/gammu-smsdrc file\ngammu-smsd-inject[2669]: Created outbox message OUTC20211204_164340_00_0664xxxxxxx_sms0.smsbackup\nWritten message with ID /var/spool/gammu/outbox/OUTC20211204_164340_00_0664xxxxxxx_sms0.smsbackup\n\n\n"
}
▍Получение SMS с помощью API
Получить сообщение с помощью нашего API тоже очень просто. Достаточно обратиться по адресу такого вида:
http://ip.of.your.pi/get.php
В ответ, в виде JSON-объекта, придут все полученные сообщения:
curl -s http://ip.of.your.pi/get.php | jq .
[
{
"id": "f0a7789a657bb34eddd17c8e64609c48",
"timestamp": 1638636342,
"year": "2021",
"month": "12",
"day": "04",
"time": "16:45",
"test": "04.12.2021 16:45:42",
"sender": "+43664xxxxxxx",
"message": "Hello bob!"
},
{
"id": "c358d0a4ca868c1d7d2eedab181eddd6",
"timestamp": 1638636414,
"year": "2021",
"month": "12",
"day": "04",
"time": "16:46",
"test": "04.12.2021 16:46:54",
"sender": "+43664xxxxxxx",
"message": "Hello "
}
]
Теперь можно интегрировать систему отправки и получения SMS-сообщений в любые проекты. Если вы это сделаете — сообщите мне. Если захотите — я могу об этом рассказать в своём блоге.
Вопросы и ответы
▍Почему тут используются PHP-скрипты собственной разработки, а не gammu-python?
Мне хотелось, чтобы код, обеспечивающий работу API, занимал бы как можно меньше места и потреблял бы как можно меньше ресурсов. Я попробовал несколько Python-реализаций подобного механизма, и ни один из них не выглядел достаточно компактным и простым. Для их использования нужно было компилировать код с множеством подпакетов. Кроме того, моему Raspberry Pi не хватало памяти на компиляцию некоторых из этих пакетов. Да и оказалось, что быстрее написать небольшой PHP-скрипт, чем заниматься оптимизацией Python-пакетов.
▍Что если пользователь отправит длинное SMS-сообщение?
API может обрабатывать сообщения, разбиваемые при отправке на части, даже в том случае, если между частями одного сообщения, находящегося в очереди, находятся другие сообщения. Поэтому данная возможность должна работать без дополнительных настроек, так как API самостоятельно собирает сообщения, разбитые на части.
▍Можно ли работать с MMS?
Gammu поддерживает MMS (Multimedia Messaging Service, служба мультимедийных сообщений). Но такие сообщения хранятся в бинарном формате, и я пока не нашёл документацию, посвящённую тому, как с ними работать. Буду весьма благодарен за любые сведения об этом.
Пригодится ли SMS-шлюз в каком-нибудь из ваших проектов?