Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

FieldTypeDescription
availablebooleanWhether the KDE Connect daemon (io.github.hepp3n.kdeconnect) is running on the session bus.
commandsAndroidCommand[]Commands paired devices can run on this PC with sh -c, from ~/.config/kdeconnect/runcommand.json.
devicesAndroidDevice[]Known devices. Unordered; sort before drawing.
transfer_progress?integerPercent 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.

FieldTypeDescription
commandstringShell command line this PC runs with sh -c when a device asks.
idstringUnique identifier, the key remove_command takes.
namestringLabel the device shows.

AndroidDevice

A remote Android or paired device known to the daemon.

FieldTypeDescription
battery?integerBattery 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?stringWhy the last browse failed, such as missing sshfs. Clears on the next browse or a mount.
charging?booleanWhether the battery is charging, or nil if unknown.
clipboard_auto_sharebooleanWhether every desktop clipboard change goes to this device. On unless switched off.
device_typeDeviceTypeDevice form-factor / category.
disabled_pluginsstring[]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.
idstringUnique KDE Connect device identifier.
mountedbooleanWhether the device’s SFTP storage is currently mounted.
namestringDevice’s advertised or configured friendly name.
pairedbooleanWhether the device has established mutual pairing.
pairing_requestedbooleanWhether 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_sentbooleanWhether 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.
reachablebooleanWhether the device is currently reachable on the network.
reconnectingbooleanWhether the device is reconnecting to apply a plugin switch. Clears when it connects again, or after 30 s.
signal_strength?integerCellular 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.

ActionArgumentsDescription
broadcastBroadcasts identity over UDP to discover nearby devices and prompt them to re-announce.
ringid: stringRings the device to locate it.
pingid: string, message?: stringSends a ping notification with an optional custom message.
send_clipboardid: string, text: stringSends text to the remote device’s clipboard.
share_clipboardid: stringSends the current desktop clipboard to the device.
send_filesid: string, files: string[]Sends existing local files to the device.
browseid: stringMounts the device’s storage via SFTP and opens it in the file manager.
unmountid: stringUnmounts the device’s SFTP storage.
pairid: stringInitiates pairing with an unpaired device.
unpairid: stringUnpairs a paired device.
accept_pairingid: stringAccepts an incoming pairing request from a device.
reject_pairingid: stringRejects an incoming pairing request from a device.
set_plugin_enabledid: string, plugin: string, enabled: booleanSwitches 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_commandname: string, command: stringOffers 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_commandid: stringStops offering the command with this commands[].id, then resends the list to every connected paired device. An unknown id does nothing.
set_clipboard_auto_shareid: string, enabled: booleanSwitches clipboard_auto_share for the device. Takes effect at the next clipboard change.

Backend

kdeconnect-service (io.github.hepp3n.kdeconnect) on the session bus.

ContractBehavior
StateTracked 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
Securitysend_files takes absolute paths of existing regular files; one relative path, missing file or directory rejects the whole batch
Daemon filesclipboard_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")

Source: supervisor/src/capabilities/android_connect/