A modern, offline-first control panel for ZTE F50 and U30 Air 5G pocket Wi-Fi.
Live speed and signal, band and cell lock, Wi-Fi and connected devices, SMS forwarding, an AT console and more.
It runs on the hotspot itself at http://192.168.0.1:2333.
Features · Screenshots · Install · Build · Develop · Credits
All screenshots come from the built-in mock device, so the numbers, messages and identifiers are made up.
The F50 and U30 Air are capable 5G hotspots, but the stock web page hides most of what the modem can do. The community project UFI-TOOLS unlocked those features with an on-device service. Over time its web page grew dense and hard to read.
AirPulse keeps the UFI-TOOLS service and replaces its entire web console with a new app, rebuilt from scratch. It starts from one question: how is my connection right now, and what can I do about it?
- Numbers come first. Speed, RSRP, SINR, RSRQ, band, PCI and EARFCN use large tabular figures that don't jump as they update. Each signal reading gets a plain-language rating.
- Controls sit next to what they change. Lock a band on the Radio page, lock a cell from the nearby-cells list, and flip Wi-Fi, IPv6 or performance mode from the overview.
- Nothing happens by surprise. Anything that can cut your connection asks first and explains what will happen. Switches wait until the device has applied the change before they settle.
- It works without internet. Fonts, icons and scripts are bundled into the APK, so the panel opens even when the SIM has no service. That is exactly when you need to change the network mode or unlock a band.
- It suits any screen. Laptops get a multi-column dashboard, phones get one column with a tab bar, and both have light and dark themes.
|
|
|
|
|
|
Note
How the IPv6 switch works. ZTE's firmware has no IPv6 switch. IPv6 follows the IP type of the active APN. AirPulse handles this for you. It sets the APN to IPv4 or IPv4v6. If the APN is chosen automatically, AirPulse first creates a manual IPv4 copy of the carrier's profile. It then waits for the redial and shows whether the carrier actually assigned an IPv6 address.
AirPulse is a single Android APK. Its package name is com.minikano.f50_sms, the same as UFI-TOOLS. It runs a small web server on port 2333 and serves the AirPulse web app from its own assets.
Supported devices: ZTE F50 and U30 Air on Unisoc firmware. Other ZTE Unisoc devices that run UFI-TOOLS may also work.
- Connect the hotspot to your computer and turn on ADB (USB debugging) on the device. How you do this depends on the firmware; the UFI-TOOLS install guide and its video tutorial (both in Chinese) cover the common cases.
- Install the APK. Either build it or download one from Actions (
app-debug.apkunder Artifacts):adb install -r app-debug.apk
- Open the app once on the device (for example through scrcpy). Allow notifications and turn off battery optimization so the service keeps running.
- Open http://192.168.0.1:2333 in a browser and sign in:
- Token: the AirPulse access token,
adminby default. AirPulse reminds you to change it until you do. - Password: the device's own admin password, as used by
192.168.0.1:8080. It is printed on the device label and is oftenadmin.
- Token: the AirPulse access token,
- From AirPulse: go to Tools › Update and upload the new APK. The service installs it and reconnects by itself.
- From UFI-TOOLS: AirPulse is a drop-in replacement because it is the same package. Android refuses an update signed with a different key, though. If your installed copy came from elsewhere, uninstall it first (
adb uninstall com.minikano.f50_sms). This resets the service's own settings, such as the access token, forwarding rules and scheduled tasks.
Requirements: Node.js 20+, JDK 17 and the Android SDK (platform 35).
git clone https://github.com/kittors/AirPulse.git
cd AirPulse
# Web app: type-check, bundle, then sync into app/src/main/assets
cd app/frontEnd
npm ci
npm run build
cd ../..
# APK (also runs the web build above)
./gradlew assembleDebug
# → app/build/outputs/apk/debug/app-debug.apkThe web build writes to app/frontEnd/dist/ first. scripts/sync-assets.mjs then copies it into app/src/main/assets/. The sync only replaces files it wrote last time; the backend's shell/ and certs/ directories next to them are never touched.
cd app/frontEnd
npm install
npm run dev # proxy /api to the device at 192.168.0.1:2333 (mock if not found)
npm run dev:mock # always use the built-in mock device
npm test # Vitest unit tests
npm run typecheck # tsc -b (strict)- To use a device at another address, set
AIRPULSE_DEVICE=http://<ip>:2333 npm run dev. - Mock device:
mock/simulates an F50 closely enough to develop every page offline. It includes live telemetry with bursty traffic, signal drift, nearby cells, SMS, forwarding channels, AT replies, the APN state machine, root and ADB tools, uploads and the update flow. It mirrors the real goform API's field names and quirks, so what works against the mock works on the device. - Non-secure context: on the device the panel is served over plain HTTP.
crypto.subtle,crypto.randomUUID, the async Clipboard API and Service Workers are therefore unavailable, and the code avoids them. Request signing uses@noble/hashes, and copying falls back toexecCommand.
| Area | Choice |
|---|---|
| UI | React 19, TypeScript (strict), Vite 6 |
| Styling | Tailwind CSS v4 with design tokens for light and dark themes |
| Components | Radix UI primitives (Dialog, Select, Switch, Tabs, Popover, Toast…) wrapped in custom components; no native <select>, checkboxes or browser dialogs |
| Data | TanStack Query: polling, optimistic updates, and waiting for the device to apply changes |
| Motion | Motion, plus View Transitions for page changes; respects Reduce motion |
| Icons and fonts | Phosphor Icons, Inter and Geist Mono, all bundled locally |
| Backend | The UFI-TOOLS Kotlin service (Ktor, on the device), unchanged |
app/
├── frontEnd/ # AirPulse web app
│ ├── src/
│ │ ├── api/ # typed clients for /api/* and the ZTE goform API
│ │ ├── model/ # pure parsing and planning logic (APN, telemetry, …)
│ │ ├── features/ # overview, radio, hotspot, messages, tools, settings
│ │ ├── components/ # UI kit (ui/) and visualizations (viz/)
│ │ └── i18n/ # English and Simplified Chinese
│ ├── mock/ # mock device for development
│ ├── tests/ # Vitest
│ ├── docs/ # design system and notes on the original API (Chinese)
│ └── scripts/sync-assets.mjs
└── src/main/ # Android app: Ktor service, shell tools, assets
API_Doc.md # backend API reference (Chinese)
docs/screenshots/ # images used in this README
- Change the default access token. Anyone on your Wi-Fi or USB network can otherwise sign in.
- The panel is meant for your local network. Don't forward port 2333 (or the web-terminal port 1146 and ADB port 5555) to the internet. Tools › Advanced › Port protection can close the terminal and ADB ports when you don't need them.
- Advanced features give the service root access to the device. Only turn them on if you understand what that means.
- "Remember me" stores only hashes in the browser, never the plain token or password.
AirPulse is built on UFI-TOOLS by MiniKano. That project provides the on-device service, the device integration and many of the features exposed here. Please consider starring and supporting the original project.
AirPulse also uses React, Vite, Tailwind CSS, Radix UI, TanStack Query, Motion, Phosphor Icons, Inter, Geist Mono, noble-hashes and uqr.
MIT. Original UFI-TOOLS © 2025 MiniKano; AirPulse web console © 2026 kittors.
AirPulse is an independent community project. It is not affiliated with or endorsed by ZTE or any carrier. Changing radio settings can break connectivity or go against your carrier's terms, so use it at your own risk.













