Pulpit bramki SMS: pasek stanu z modemem, siecią, sygnałem 70 procent i aktywną kartą SIM1, kafelki z liczbą wysłanych i odebranych wiadomości, wykres z ostatnich 24 godzin i lista ostatnich wiadomości z rozmytymi numerami

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.

Pulpit bramki SMS: pasek stanu z modemem, siecią, sygnałem 70 procent i aktywną kartą SIM1, kafelki z liczbą wysłanych i odebranych wiadomości, wykres z ostatnich 24 godzin i lista ostatnich wiadomości z rozmytymi numerami(powiększ)
Pulpit bramki: stan modemu, ruch z dziś i ostatnie wiadomości

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.

Lista Klienci i klucze: trzy aktywne wpisy z przypisaną kartą SIM, adresem webhooka, liczbą kluczy i ruchem z 7 dni, nazwa jednego klienta i adresy rozmyte(powiększ)
Klienci bramki z kartą SIM, webhookiem i kluczami

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.

Lista Wiadomości z filtrami po kliencie, kierunku, stanie i numerze, wśród wpisów testowa wiadomość z polskimi znakami wysłana w trzech częściach i oznaczona jako doręczona(powiększ)
Wiadomość z polskimi znakami wysłana w trzech częściach i doręczona

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.

Lista webhooków: zdarzenia message.received, message.delivered, message.sent i webhook.test, każde dostarczone za pierwszym razem z odpowiedzią HTTP 204, jedno rozwinięte z treścią JSON(powiększ)
Webhooki dostarczone do klienta, z treścią zdarzenia

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.
Statystyki z 30 dni: 233 wysłane wiadomości, zero błędów, 12 odebranych i słupkowy wykres dzienny z podziałem na klientów(powiększ)
Statystyki z 30 dni według klienta
Dziennik zdarzeń: utworzenie i zmiana klientów oraz konfiguracja logowania dwuetapowego, z datą, kontem, szczegółami w JSON i rozmytym adresem IP(powiększ)
Dziennik zdarzeń z działaniami administratorów

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.

Kliknij albo przewiń, żeby przybliżyć. Przeciągnij, żeby przesunąć.Stuknij dwa razy albo rozsuń palce, żeby przybliżyć.

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