🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Tether API
Android app that exposes your phone's connection status via HTTP API, accessible from tethered devices.
Features
- Auto-start on tether: Server automatically starts/stops when tethering is enabled/disabled
- Real-time status: Connection type (5G/LTE/3G/WIFI), signal strength, carrier, battery level
- Simple REST API: JSON responses, easy to integrate with any client
- No root required: Works with standard Android permissions
Installation
Build with Android Studio or use the release APK.
Required permissions:
READ_PHONE_STATE- Network connection type and carrierACCESS_FINE_LOCATION- Required by Android 10+ for signal strengthPOST_NOTIFICATIONS- Service status notification
API Reference
Default port: 8765
Common gateway IPs when tethering:
- USB tethering:
192.168.42.1 - WiFi hotspot:
192.168.43.1
GET /status
Returns full phone status.
Response:
{
"connection": {
"type": "LTE",
"signal_dbm": -89,
"signal_bars": 3,
"carrier": "T-Mobile",
"operator": "310260",
"roaming": false
},
"battery": {
"level": 73,
"charging": false
},
"network": {
"wifi_ssid": null,
"ip_addresses": ["192.168.42.129"]
},
"timestamp": 1702915200
}
Fields:
| Field | Description |
|---|---|
connection.type |
5G, LTE, HSPA, 3G, 2G, WIFI, CELLULAR, NONE |
connection.signal_dbm |
Signal strength in dBm (-999 if unavailable) |
connection.signal_bars |
0-4 signal bars |
connection.carrier |
Network operator name |
connection.operator |
MCC+MNC code |
connection.roaming |
Whether roaming is active |
battery.level |
Battery percentage (0-100) |
battery.charging |
Whether device is charging |
network.wifi_ssid |
WiFi SSID if connected via WiFi |
network.ip_addresses |
List of device IP addresses |
timestamp |
Unix timestamp of response |
GET /health
Health check endpoint.
Response:
{"status": "ok"}
GET /
API information.
Response:
{
"name": "Tether API",
"version": "1.0.0",
"endpoints": [
{"path": "/status", "method": "GET", "description": "Get phone status"},
{"path": "/health", "method": "GET", "description": "Health check"}
]
}
Client Examples
Basic curl
curl http://192.168.42.1:8765/status
Auto-discover phone IP
#!/bin/bash
for ip in 192.168.42.1 192.168.43.1 192.168.44.1; do
if curl -s --connect-timeout 1 "http://$ip:8765/health" | grep -q "ok"; then
echo "$ip"
exit 0
fi
done
exit 1
Waybar module
~/.config/waybar/config:
{
"custom/phone": {
"exec": "~/.local/bin/phone-status.sh",
"return-type": "json",
"interval": 10,
"format": "{}"
}
}
~/.local/bin/phone-status.sh:
#!/bin/bash
PHONE_IP="${PHONE_IP:-192.168.42.1}"
PORT="${PORT:-8765}"
status=$(curl -s --connect-timeout 2 "http://$PHONE_IP:$PORT/status")
if [ -z "$status" ]; then
echo '{"text": "N/A", "tooltip": "Phone not connected", "class": "disconnected"}'
exit 0
fi
type=$(echo "$status" | jq -r '.connection.type')
bars=$(echo "$status" | jq -r '.connection.signal_bars')
battery=$(echo "$status" | jq -r '.battery.level')
case $bars in
4) signal="▂▄▆█" ;;
3) signal="▂▄▆░" ;;
2) signal="▂▄░░" ;;
1) signal="▂░░░" ;;
*) signal="░░░░" ;;
esac
echo "{\"text\": \"$type $signal $battery%\", \"tooltip\": \"$type | Signal: $bars/4 | Battery: $battery%\", \"class\": \"$type\"}"
Python client
import requests
def get_phone_status(ip="192.168.42.1", port=8765):
try:
r = requests.get(f"http://{ip}:{port}/status", timeout=2)
return r.json()
except:
return None
status = get_phone_status()
if status:
print(f"Connection: {status['connection']['type']}")
print(f"Signal: {status['connection']['signal_bars']}/4")
print(f"Battery: {status['battery']['level']}%")
KDE Plasmoid / Generic widget
Most desktop widgets can execute shell commands. Use the curl/jq approach:
curl -s http://192.168.42.1:8765/status | jq -r '"\(.connection.type) \(.connection.signal_bars)/4 \(.battery.level)%"'
Building
Debug build
./gradlew assembleDebug
# Output: app/build/outputs/apk/debug/app-debug.apk
Release build
- Generate a keystore:
keytool -genkey -v -keystore keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
- Build with signing:
export KEYSTORE_PASSWORD="your-password"
export KEY_ALIAS="release"
export KEY_PASSWORD="your-key-password"
./gradlew assembleRelease
# Output: app/build/outputs/apk/release/app-release.apk
Contributing
See CONTRIBUTING.md for guidelines.
License
GPLv3 - see LICENSE
Languages
Kotlin
87.7%
Just
12.3%