android_connect
Android device synchronization through KDE Connect: battery, cellular signal, find my phone, clipboard sync, file sharing, and SFTP mount.
list {
items = mantle.android_connect:map(function(android)
return android.devices
end),
key = function(device) return device.id end,
each = function(device)
local battery = device.battery and string.format(" %d%%", device.battery) or ""
return rect {
on_click = function() mantle.android_connect:ring(device.id) end,
children = { text { content = device.name .. battery } },
}
end,
}
State
mantle.android_connect:get() returns AndroidConnectState, nil before the first push. A field marked ? may be absent.
| Field | Type | Description |
|---|---|---|
available | boolean | Whether the KDE Connect daemon (io.github.hepp3n.kdeconnect) is running on the session bus. |
commands | AndroidCommand[] | Commands paired devices can run on this PC with sh -c, from ~/.config/kdeconnect/runcommand.json. |
devices | AndroidDevice[] | Known devices. Unordered; sort before drawing. |
transfer_progress? | integer | Percent sent of the file transfer in progress, or nil when none runs. The daemon does not say which device it goes to. |
AndroidCommand
A command a paired device can run on this PC.
| Field | Type | Description |
|---|---|---|
command | string | Shell command line this PC runs with sh -c when a device asks. |
id | string | Unique identifier, the key remove_command takes. |
name | string | Label the device shows. |
AndroidDevice
A remote Android or paired device known to the daemon.
| Field | Type | Description |
|---|---|---|
battery? | integer | Battery level percentage (0 to 100), or nil if unknown or unannounced. With two devices connected, the daemon can report one’s value as the other’s. |
browse_error? | string | Why the last browse failed, such as missing sshfs. Clears on the next browse or a mount. |
charging? | boolean | Whether the battery is charging, or nil if unknown. |
clipboard_auto_share | boolean | Whether every desktop clipboard change goes to this device. On unless switched off. |
device_type | DeviceType | Device form-factor / category. |
disabled_plugins | string[] | Plugins switched off for this device, by id: battery, clipboard, connectivity_report, contacts, findmyphone, mpris, notification, ping, runcommand, sftp, share, sms, systemvolume, telephony, or the daemon’s default mousepad and presenter. |
id | string | Unique KDE Connect device identifier. |
mounted | boolean | Whether the device’s SFTP storage is currently mounted. |
name | string | Device’s advertised or configured friendly name. |
paired | boolean | Whether the device has established mutual pairing. |
pairing_requested | boolean | Whether an incoming pairing request from this device is awaiting confirmation. Clears on accept, reject or disconnect, or after 30 s: the daemon reports no cancel from the device. |
pairing_sent | boolean | Whether a pairing request this PC sent is awaiting the device’s answer. Clears when the device accepts, on unpair, on disconnect, or after 30 s: the daemon reports no rejection. |
reachable | boolean | Whether the device is currently reachable on the network. |
reconnecting | boolean | Whether the device is reconnecting to apply a plugin switch. Clears when it connects again, or after 30 s. |
signal_strength? | integer | Cellular network signal strength (0 to 4 bars), or nil if not reported. With two devices connected, the daemon can report one’s value as the other’s. |
DeviceType
The device category reported by KDE Connect.
One of "phone", "tablet", "desktop", "laptop", "tv", "unknown".
Actions
Call each as mantle.android_connect:<action>(arguments...); ? marks an argument you may omit.
| Action | Arguments | Description |
|---|---|---|
broadcast | Broadcasts identity over UDP to discover nearby devices and prompt them to re-announce. | |
ring | id: string | Rings the device to locate it. |
ping | id: string, message?: string | Sends a ping notification with an optional custom message. |
send_clipboard | id: string, text: string | Sends text to the remote device’s clipboard. |
share_clipboard | id: string | Sends the current desktop clipboard to the device. |
send_files | id: string, files: string[] | Sends existing local files to the device. |
browse | id: string | Mounts the device’s storage via SFTP and opens it in the file manager. |
unmount | id: string | Unmounts the device’s SFTP storage. |
pair | id: string | Initiates pairing with an unpaired device. |
unpair | id: string | Unpairs a paired device. |
accept_pairing | id: string | Accepts an incoming pairing request from a device. |
reject_pairing | id: string | Rejects an incoming pairing request from a device. |
set_plugin_enabled | id: string, plugin: string, enabled: boolean | Switches one of the device’s plugins on or off; disabled_plugins lists the ids. The daemon drops the connection so the device learns the change, and the device reconnects. |
add_command | name: string, command: string | Offers paired devices a name button that runs command on this PC with sh -c. Saved to runcommand.json, then the list is resent to every connected paired device. |
remove_command | id: string | Stops offering the command with this commands[].id, then resends the list to every connected paired device. An unknown id does nothing. |
set_clipboard_auto_share | id: string, enabled: boolean | Switches clipboard_auto_share for the device. Takes effect at the next clipboard change. |
Backend
kdeconnect-service (io.github.hepp3n.kdeconnect) on the session bus.
| Contract | Behavior |
|---|---|
| State | Tracked via io.github.hepp3n.kdeconnect.Daemon. Without a running daemon, available is false, devices is empty, and every action is a no-op. When the daemon appears, the capability asks it to broadcast its identity so nearby devices answer |
| Security | send_files takes absolute paths of existing regular files; one relative path, missing file or directory rejects the whole batch |
| Daemon files | clipboard_auto_share is read from, and set_clipboard_auto_share rewrites only the autoShare line of, ~/.config/kdeconnect/<id>/kdeconnect_clipboard/config. An id holding anything but letters, digits, _ and - is refused. commands is read from, and add_command and remove_command rewrite, ~/.config/kdeconnect/runcommand.json; a file that does not parse is left untouched |
Installing the daemon
Only kdeconnect-service is needed, not the COSMIC applet. Install it with Rust and the libpulse
and dbus development headers:
cargo install --git https://github.com/cosmic-utils/kdeconnect kdeconnect-service
Run the same command to update: it rebuilds only when upstream has new commits. Leave out
--locked: upstream’s Cargo.lock lags its own crate versions, so cargo refuses it.
Start it from the compositor, so it has the Wayland socket that clipboard sync needs. In niri’s
config.kdl:
spawn-sh-at-startup "~/.cargo/bin/kdeconnect-service"
In Hyprland’s hyprland.conf:
exec-once = ~/.cargo/bin/kdeconnect-service
Open TCP and UDP ports 1714–1764 in the firewall, and stop KDE’s kdeconnectd if it runs: both
bind port 1716.
How do I…
Find my phone
Call ring with the target device’s id:
mantle.android_connect:ring(device.id)
Send clipboard text
mantle.android_connect:send_clipboard(device.id, "Hello from desktop")
Show why browsing failed
browse reports no error itself. When the mount fails, browse_error names the cause until the
next browse or a successful mount:
text { content = device.browse_error or "" }
Stop syncing the clipboard to a device
mantle.android_connect:set_clipboard_auto_share(device.id, not device.clipboard_auto_share)
Offer a command to my phone
Any paired device can then run it on this PC, as sh -c, from its Run command screen:
mantle.android_connect:add_command("Lock screen", "loginctl lock-session")