
Programowanie
Własna bramka SMS: modem GSM, dwie karty i API dla wielu systemów
Moje systemy wysyłają SMS-y o przerwanych transmisjach, awariach tablic i zmianach w kopiach zapasowych. Zamiast płacić za komercyjną bramkę, zbudowałem własną: modem Quectel M95, dwie karty SIM, API w Pythonie, podpisane webhooki, raporty doręczenia i panel z logowaniem dwuetapowym.
5.11.2026
Kilka moich systemów musi czasem kogoś szybko zaalarmować. Transmisje ChurchStream informują o przerwanym streamie, tablice informacyjne i kiosk zgłaszają awarie, a nocne kopie zapasowe wykrywają zmiany w kodzie stron.
E-mail łatwo przeoczyć, a komercyjne bramki SMS liczą za każdą wiadomość. Dlatego zbudowałem własną bramkę: mały modem GSM w homelabie i usługę, która udostępnia go innym systemom przez API.
(powiększ)Sprzęt: modem za kilkadziesiąt złotych
Sercem bramki jest płytka a-GSM II Shield z modemem Quectel M95. Ma dwa gniazda kart SIM, a z serwerem łączy się przez USB. Obie karty są w sieci Play: jedna dla moich systemów, druga dla aplikacji instytucji.
Modem rozmawia komendami AT przez port szeregowy. Usługa pilnuje, czy modem odpowiada, czy jest zarejestrowany w sieci i jaki ma sygnał. Publiczny stan, bez klucza, odświeża się co 30 sekund i służy do monitoringu.
Wersja 1.0: prosta usługa w Pythonie
Pierwsza wersja powstała w marcu 2026 roku. Robiła to, czego potrzebowały moje skrypty:
- wysyłka pojedynczych SMS-ów i wysyłka masowa przez kolejkę w Redis,
- historia wiadomości w bazie, z filtrami po numerze, dacie i kierunku,
- przełączanie kart SIM i odbiór wiadomości z nieaktywnej karty co 5 minut,
- limit zapytań na adres IP,
- zgodność z API komercyjnej bramki SMSEagle, więc gotowe integracje działały bez zmian.
Wersja 2.0: wielu klientów na jednej bramce
Z bramki zaczęły korzystać kolejne aplikacje, w tym panel SMS dla ludzi. Jeden wspólny klucz API przestał wystarczać, więc w listopadzie przebudowałem bramkę. Klient to teraz aplikacja albo grupa integracji. Każdy ma:
- przypisaną kartę SIM, z której wysyła i na którą dostaje odpowiedzi,
- własne klucze API z uprawnieniami do wysyłki, odczytu, stanu i kart SIM,
- opcjonalny dobowy limit wiadomości,
- własny adres webhooka na zdarzenia.
Klucz jest pokazywany tylko raz, przy tworzeniu. W bazie zostaje jego skrót SHA-256. Stary klucz z pliku konfiguracyjnego należy do klienta „Integracje dotychczasowe”, więc nic, co już działało, nie wymagało zmian.
(powiększ)Długie wiadomości i polskie znaki
Bramka pracuje w trybie PDU. Długa wiadomość idzie jako jeden SMS wieloczęściowy, który telefon skleja w całość. Tekst z polskimi literami jest kodowany w UCS-2, czyli mieści 70 znaków w jednym SMS-ie i 67 w każdej części dłuższej wiadomości.
Każdy SMS jest wysyłany z prośbą o raport doręczenia. Dzięki temu bramka wie nie tylko, że przekazała wiadomość do sieci, ale też, że dotarła do telefonu.
(powiększ)Podpisane webhooki
Zamiast odpytywać bramkę, aplikacja może dostawać zdarzenia: odebrany SMS, wysłany, doręczony, niedoręczony, błąd albo zmiana stanu bramki. Każde zdarzenie jest podpisane HMAC-SHA256 z czasem wysłania, więc klient sprawdzi, że pochodzi z bramki i nie jest powtórką.
Gdy klient nie odpowiada, bramka ponawia po 10 sekundach, 30 sekundach, 2, 10 i 30 minutach, a potem co godzinę. Po około 6 godzinach oznacza zdarzenie jako nieudane, a w panelu można je ponowić ręcznie.
(powiększ)Panel administracyjny
Bramka ma prosty panel, renderowany po stronie serwera. Logowanie wymaga hasła i kodu z aplikacji uwierzytelniającej, a sesje są zapisane w bazie. W panelu są:
- pulpit z ruchem na obu kartach i wykresem z ostatnich 24 godzin,
- klienci i klucze API,
- wszystkie wiadomości z filtrami,
- statystyki dzienne według klienta i karty,
- webhooki z treścią zdarzeń i ręcznym ponawianiem,
- dziennik zdarzeń: logowania, zmiany klientów i kluczy, nietypowe komunikaty z modemu.
(powiększ)
(powiększ)Ostrożnie z wysyłką masową
Operator blokuje karty, które w krótkim czasie wysyłają dużo wiadomości, i nie podaje progu. Dlatego wysyłka zbiorcza jest celowo powolna:
- około 8 sekund przerwy między wiadomościami, z losowym rozrzutem,
- 5 minut pauzy po każdych 50 wiadomościach,
- najwyżej 200 części SMS na godzinę.
Powiadomienie do kilkuset osób trwa więc dłużej, ale karta nie zostanie zablokowana w środku wysyłki.
Bramka SMS to mały modem i trochę cierpliwości. Najtrudniejsze nie było wysłanie wiadomości, tylko pewność, że dotarła i że operator nie zablokuje karty.
- Aleksander Kowal
Co dalej
Dla ludzi, którzy nie chcą korzystać z API, powstał panel SMS do wysyłki powiadomień z przeglądarki. W planach mam pobieranie treści MMS, bo bramka już rozpoznaje powiadomienia o nich, oraz węzeł sieci LoRa jako osobną aplikację obok bramki.
Stack
- Python, FastAPI i pyserial do obsługi modemu komendami AT,
- SQLAlchemy z SQLite, Redis jako kolejka wysyłki masowej,
- panel w Jinja2, hasła argon2id, kody TOTP,
- Docker na serwerze w homelabie.
Przydał się ten tekst?
Nowe teksty na maila
Raz na jakiś czas, gdy pojawi się nowy wpis albo projekt. Bez reklam.
Masz pomysł, pytanie albo uwagę? Napisz do mnie:Włącz JavaScript, żeby zobaczyć adres e-mail
