- Add GPLv3 LICENSE file - Add CONTRIBUTING.md with guidelines - Update README: remove Play Store section, add license and contributing refs - Fix feature graphic (remove grid lines, replace Waybar chip with No root)
218 lines
4.8 KiB
Markdown
218 lines
4.8 KiB
Markdown
# 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 carrier
|
|
- `ACCESS_FINE_LOCATION` - Required by Android 10+ for signal strength
|
|
- `POST_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:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{"status": "ok"}
|
|
```
|
|
|
|
### GET /
|
|
|
|
API information.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```bash
|
|
curl http://192.168.42.1:8765/status
|
|
```
|
|
|
|
### Auto-discover phone IP
|
|
|
|
```bash
|
|
#!/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:**
|
|
```json
|
|
{
|
|
"custom/phone": {
|
|
"exec": "~/.local/bin/phone-status.sh",
|
|
"return-type": "json",
|
|
"interval": 10,
|
|
"format": "{}"
|
|
}
|
|
}
|
|
```
|
|
|
|
**~/.local/bin/phone-status.sh:**
|
|
```bash
|
|
#!/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
|
|
|
|
```python
|
|
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:
|
|
|
|
```bash
|
|
curl -s http://192.168.42.1:8765/status | jq -r '"\(.connection.type) \(.connection.signal_bars)/4 \(.battery.level)%"'
|
|
```
|
|
|
|
## Building
|
|
|
|
### Debug build
|
|
|
|
```bash
|
|
./gradlew assembleDebug
|
|
# Output: app/build/outputs/apk/debug/app-debug.apk
|
|
```
|
|
|
|
### Release build
|
|
|
|
1. Generate a keystore:
|
|
```bash
|
|
keytool -genkey -v -keystore keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias release
|
|
```
|
|
|
|
2. Build with signing:
|
|
```bash
|
|
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](CONTRIBUTING.md) for guidelines.
|
|
|
|
## License
|
|
|
|
GPLv3 - see [LICENSE](LICENSE)
|