Using KDEConnect to *Comfortably* Send SMS Messages from the Shell

Have you ever wondered how far can you go in connecting your Android phone to your computer? The Android ecosystem is open source and assuming you use GNU/Linux, what could possibly get in your way? Anyway, I hope you’ve heard of KDEConnect.

In this article, I’ll explain how I’ve managed to create a simple shell function I called sms that works like this:

1st example of sms shell function

Super cool right? In this article I'll explain in full details how this is done.

Basically, this shell function integrates 3 programs:

KDEConnect is probably the most important peice of the puzzle - it allows you to connect your Android phone to your computer in various ways. You install the app on your phone and the 'server' on your Linux machine (not necessarily running KDE) and while the daemon is running (usually installed here: /usr/lib/kdeconnectd) you can pair your phone (pairing is only needed in the beginning) and you get all of the following fruits and vegetables:

And this is only a partial list of KDEConnect’s features, to quote from the official website:

KDE Connect is a project to communicate across all your devices. For example, with KDE Connect you can receive your phone notifications on your desktop computer, control music playing on your desktop from your phone, or use your phone as a remote control for your desktop.

I'll put together 3 programs into a puzzle 1 by 1:

KDEConnect’s built-in SMS interface

First, allow me to explain about the architecture of KDEConnect:

A proper installation of KDEConnect provides an executable with a daemon called kdeconnectd. As I said before KDEConnect is mainly intended to be used on Linux machines running the KDE desktop environment and as such it’s native client. Fortunately, anyone can write a client that will communicate with the daemon and make it easier to communicate with your phone through the daemon through a GUI or a CLI.

The basic client for the daemon usually provided with KDEConnect's installation is called kdeconnect-cli.

Using kdeconnect-cli

This is it's usage/help output:

$ kdeconnect-cli --help
Usage: kdeconnect-cli [options]
KDE Connect CLI tool

Options:
  -l, --list-devices            List all devices
  -a, --list-available          List available (paired and reachable) devices
  --id-only                     Make --list-devices or --list-available print
                                only the devices id, to ease scripting
  --refresh                     Search for devices in the network and
                                re-establish connections
  --pair                        Request pairing to a said device
  --ring                        Find the said device by ringing it.
  --unpair                      Stop pairing to a said device
  --ping                        Sends a ping to said device
  --ping-msg <message>          Same as ping but you can set the message to
                                display
  --share <path>                Share a file to a said device
  --list-notifications          Display the notifications on a said device
  --lock                        Lock the specified device
  --send-sms <message>          Sends an SMS. Requires destination
  --destination <phone number>  Phone number to send the message
  --device, -d <dev>            Device ID
  --name, -n <name>             Device Name
  --encryption-info             Get encryption info about said device
  --list-commands               Lists remote commands and their ids
  --execute-command <id>        Executes a remote command by id
  -k, --send-keys               Sends keys to a said device
  --my-id                       Display this device's id and exit
  -h, --help                    Displays this help.
  -v, --version                 Displays version information.
  --author                      Show author information.
  --license                     Show license information.
  --desktopfile <file name>     The base file name of the desktop entry for
                                this application.

As you can see, there is a built-in interface to send an SMS message through this command, using --send-sms <message> together with --destination <phone number>. So what is there left to do? Just bundle all of these together into a shell function right?

The problem that's still need to be solved is:

Up until this point I’ve never thought I’ll be able to solve this problem gracefully without creating a monolithic daemon client in a compiled language like C or C++ which uses KDEConnect's libraries and that communicating directly with kdeconnectd. What solved this problem extremely easily and what made me feel like a command line super hero was introducing fzf and khard.

khard: An address book for the Linux console

Quoting from the github page:

It creates, reads, modifies and removes carddav address book entries at your local machine. Khard is also compatible to the email clients mutt and alot and the SIP client twinkle. You can find more information about khard and the whole synchronization process here.

My setup consists of vdirsyncer syncing my contacts with my Nextcloud instance sitting on my VPS by https://ovh.com/. Read more about the synchronization and usage of the 2 programs in vdirsyncer's documentation: https://vdirsyncer.pimutils.org/en/stable/.

The command used to display phone numbers only is khard phone, using the --parsable flag to produce tab seperated output, comfortable for processing it's output by a shell.

FZF: A command-line fuzzy finder

Quoting from the github page:

It's an interactive Unix filter for command-line that can be used with any list; files, command history, processes, hostnames, bookmarks, git commits, etc.

Originally FZF was intended for finding files in your computer. What it does with any list it receives is opening a curses user interface in which you type a search term and while you type, if you press Enter while the choice-indicator is placed on the desired result, it is printed on stdout. Here is an ASCIInema screen cast of fzf running with an input of the command find /usr/share/:

basic usage of FZF

Combining everything together

The following shell function is used in my dotfiles repository in the file .functions.

sms(){
    local args="$@"
    local phone_number name phone_type
    khard phone --parsable | sort -u | fzf | IFS=$'\t' read -r phone_number name phone_type
    if [[ -z "${phone_number}" ]]; then
        echo No recipient was chosen >&2
        return
    else
        echo "${name}"$'\t'"${phone_number}"$'\t'"${phone_type}" > ${_KDECONNECT_SMS_LAST_RECIPIENT}
        kdeconnect-cli --device ${_KDECONNECT_DEFAULT_DEVICE} --send-sms "${args}" --destination "${phone_number}" && \
        echo sent sms message to ${name} | fribidi
    fi
}

Final notes

In order to conceal the private information of my real contacts, I used a list of randomly generated names as contacts and another list of randomly generated numbers so the actual shell function used to produce the ASCIInema recordings was this:

sms(){
    paste -d$'\t' ~/numbers.txt ~/names.txt | fzf | while IFS=$'\t' read -r name number; do
        echo sending sms message to $name
    done
}

The list of names is sponsored by http://listofrandomnames.com/index.cfm and the list of numbers was generated by shuf -i 100000-999999 -n 150 -o ~/numbers.txt which is part of GNU's coreutils.