Files
tajniak81 d6956395c8 Rebrand CommGate to gsmnode and adopt new design system
Rename the whole project from CommGate to gsmnode and re-skin every
surface onto the new signal-green + ink design system (Space Grotesk /
IBM Plex Sans / JetBrains Mono, flat surfaces, two-arrow routing mark,
lowercase gsm+node wordmark).

- Web App + API panel: rewrite token layer, gn-* utilities, data-gsm-theme,
  new logo/favicon assets; both builds verified.
- Phone App (Flutter): green theme, new mark/wordmark widgets, adaptive
  launcher icons; rename Android applicationId/package to app.gsmnode.phone;
  bump AGP to 8.6.0 and google_fonts to 8.1.0; debug APK build verified.
- API Server (Go): panel routes, User-Agent, health service id, branding.
- Home Assistant Plugin: rename integration domain sms_gateway to gsmnode
  (folder, DOMAIN, services, entity ids, classes); py_compile verified.
- Update all READMEs; add .gitignore (excludes Design/, build artifacts).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 08:53:14 +02:00

5.5 KiB

gsmnode — Phone App (Flutter / Android)

Turns an Android phone into the SMS gateway endpoint. It registers with the API Server, polls for pending outbound messages, sends them over the radio, reports delivery state, and forwards incoming SMS to the server's inbox.

API Server  ──(pending messages)──►  Phone App  ──►  SmsManager (send)
     ▲                                    │
     └──(status reports / inbox)──────────┘  ◄── BroadcastReceiver (incoming)

The Phone App talks only to the API Server (never to PocketBase directly), using the device token issued at registration.

Status: source scaffold

This folder contains the complete Dart app and the native Android (Kotlin) SMS bridge, but not the generated Gradle/platform scaffolding. You generate that once with flutter create after installing the toolchain (below).

Prerequisites

  1. Flutter SDK (stable) — https://docs.flutter.dev/get-started/install/windows
  2. JDK 17 (this machine currently has only JDK 8 — Android Gradle needs 17+)
  3. Android SDK (via Android Studio or flutter doctor --android-licenses)
  4. A physical Android phone with a SIM (the emulator can't send real SMS)

Verify with flutter doctor — resolve anything it flags before continuing.

Generate platform scaffolding & wire in the native code

From this Phone App/ folder:

# 1. Generate the android/ Gradle project (keeps lib/ and pubspec.yaml)
flutter create . --org app.smsgateway --project-name sms_gateway_phone --platforms=android

# 2. Overlay the SMS-enabled manifest + Kotlin (overwrites the generated stubs)
Copy-Item -Recurse -Force android_overlay/* android/

# 3. Fetch packages
flutter pub get

android_overlay/ mirrors the real android/ paths, so step 2 drops:

  • app/src/main/AndroidManifest.xml — SMS permissions + the SmsReceiver
  • app/src/main/kotlin/app/smsgateway/sms_gateway_phone/MainActivity.kt — send bridge
  • app/src/main/kotlin/app/smsgateway/sms_gateway_phone/SmsReceiver.kt — incoming bridge

If Gradle complains about SDK levels, set minSdkVersion 23 (or higher) in android/app/build.gradle.

Run

flutter devices            # confirm your phone is listed
flutter run                # build & install on the connected phone
  1. On first launch, enter the API Server URL, your email/password, and a device name, then tap Sign in & register device.
    • Emulator → host: use http://10.0.2.2:8080.
    • Physical phone → use the host's LAN IP, e.g. http://10.2.1.x:8080 (the same network as the phone; make sure the API Server is reachable).
  2. On the home screen, grant SMS & phone permissions, then Start gateway.
  3. Send a test message from the Web App → it appears in the activity log and is delivered via the phone. Texts received by the phone show up in the Web App Inbox.

How it maps to the API

Action Endpoint
Login POST /api/auth/login (JWT)
Register device POST /api/mobile/v1/device → device token
Poll pending GET /api/mobile/v1/messages (marks them Processed)
Report state PATCH /api/mobile/v1/messages/{id} (Sent/Failed)
Incoming SMS POST /api/mobile/v1/inbox
Heartbeat POST /api/mobile/v1/ping

Pulled items carry a type of sms or call. For call the app places a native phone call via TelecomManager.placeCall (needs CALL_PHONE — covered by the phone permission) instead of sending SMS, then reports Sent. TelecomManager.placeCall is used rather than startActivity(ACTION_CALL) so the call still goes through when the screen is locked / the app is backgrounded (Android blocks background activity starts, but not telecom-routed calls).

Code layout

lib/
  main.dart                 entry, bootstraps services, picks first screen
  config.dart               default API base + poll/ping intervals
  models/message.dart       outbound message model
  services/
    storage.dart            persisted settings/tokens (shared_preferences)
    api_client.dart         API Server HTTP client
    sms_service.dart        platform-channel bridge (send + incoming stream)
    gateway_service.dart    the poll → send → report loop + inbox forwarding
  screens/
    login_screen.dart       login + device registration
    home_screen.dart        start/stop, permissions, activity log
android_overlay/            native Android files to copy after `flutter create`

Background & delivery reports (implemented)

  • Foreground service (GatewayForegroundService.kt): starting the gateway launches an ongoing-notification foreground service + partial wakelock, so the poll/send loop keeps running while the screen is off or the app is backgrounded.
  • Delivery reports: MainActivity.sendSms attaches sent/delivered PendingIntents; SmsStatusReceiver forwards the outcome (tagged with the message id) to Dart, which reports Delivered/Failed to the API Server.

Known next steps (not yet implemented)

  • Survive full task removal / long Doze: the foreground service covers screen-lock, but surviving the user swiping the app away or hours of Doze would need a dedicated background Dart isolate (e.g. flutter_background_service).
  • Push wake-up (FCM): register an FCM token at registration so the server can wake the device instead of polling. Requires a Firebase project + google-services.json + server-side FCM sending in the API Server.
  • MMS / data SMS: only text SMS is implemented.