Unlike an Android or IOS phone, the Librem 5 and Liberty Phones are easily scriptable. You can bend your phone software to your will in a variety of programming languages. In this Easy App Development blog, we’ll cover making a text-forwarding system. Elements covered here can be used to take full control of your phone’s texting capabilities.
On PureOS, messaging (including texting), is handled with our app Chatty. This is a convenient way to handle Matrix chat, XMPP, and Texting all in the same place. For text messaging, Chatty uses the backend called mmcli
, and mmcli
is where all the magic really happens. You can get an idea of what mmcli
can do by running man mmcli
. At the end of the day, mmcli
is what will be talking to our modem.
Playing around with mmcli
, I noticed texts don’t stay on the modem; another process was handling these and removing them. Of course, this is just Chatty processing texts as it adds them to the user interface. As a simple way to quit chatty, the killall
command is quick and dirty.
# Bash for i in 1 2 3; do killall chatty; done # Python for i in range(0,10): os.system("killall chatty")
With Chatty stopped, we are free to use mmcli
to read what messages are on the modem.
mmcli -m any --messaging-list-sms /org/freedesktop/ModemManager1/SMS/0 (received)
This shows that we have an SMS with the ID of 0 on the modem. Use the following to get the details, including message text.
mmcli -s <TEXT_ID> --output-keyvalue
To read our SMS with ID 0, the following outputs the data in an easily parsable manner.
$ mmcli -s 0 --output-keyvalue sms.dbus-path : /org/freedesktop/ModemManager1/SMS/0 sms.content.number : +1111111111 sms.content.text : Test sms.content.data : -- sms.properties.pdu-type : deliver sms.properties.state : received sms.properties.validity : -- sms.properties.storage : me sms.properties.smsc : +140500000 sms.properties.class : -- sms.properties.teleservice-id : -- sms.properties.service-category : -- sms.properties.delivery-report : -- sms.properties.message-reference : -- sms.properties.timestamp : 2024-01-12T10:18:52-08 sms.properties.delivery-state : -- sms.properties.discharge-timestamp : --
In Python, you can get messages ready for reading with:
def get_new_messages(raw_messages): received_ids = [] for line in raw_messages.split("\n"): if "received" in line: received_ids.append(int(line.split("/")[-1].split(" ")[0])) return(received_ids)
And read that data with:
def get_text_by_id(ID): cmd = ['mmcli', '-s', str(ID), '--output-keyvalue'] result = subprocess.check_output(cmd, stderr=subprocess.STDOUT) sender_phone = "" for line in result.decode().split('\n'): if line.startswith("sms.content.number"): sender_phone = line.split("sms.content.number")[-1] sender_phone = sender_phone.strip() sender_phone = sender_phone[2:] sender_phone = sender_phone.replace('\\n','\n') # TODO check sender_phone and return if line.startswith("sms.content.text"): text = line.split("sms.content.text")[-1] text = text.strip() text = text[2:] text = text.replace('\\n','\n') return(sender_phone, text)
Once you read and process a text, you need to remove it from the modem. If you don’t do this, things will get messy very fast.
Remove message from modem
sudo mmcli -m any --messaging-delete-sms=<TEXT_ID>
Remove message with ID 0
sudo mmcli -m any --messaging-delete-sms=0
To send a message, we need to create the message on the modem and then use the mmcli
send option on your newly created Text ID. Finally, we need to remove the sent text from the modem. In bash that looks like this:
text="Example Text" phone="+12223334444" message_values="text='$text',number='$phone'" # Send text to the modem output=$(sudo mmcli -m any --messaging-create-sms="$message_values") # Send SMS message_id=$(echo $output | rev | cut -d '/' -f1 | rev) sudo mmcli -s $message_id --send # Remove the sent message from the modem sudo mmcli -m any --messaging-delete-sms=$message_id
For programmatic email forwarding, an email service that supports SMTP, like our Librem.one service, is very handy. In python something like this works perfectly.
import smtplib import ssl from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart ############### Needed Email Info ############## #################### Target #################### FOWARD_EMAIL="" #Email to forward texts to # #################### SENDER #################### ACCOUNT="" #Email to use for sending # ACCOUNT_PASSWD="" #Account Password # ACCOUNT_SMTP="" #SMTP host # ACCOUNT_PORT = 465 #SMTP port # ################################################ def send_email(title, message_text): message = MIMEMultipart("alternative") message["Subject"] = title message["From"] = ACCOUNT message["To"] = FOWARD_EMAIL message.attach(MIMEText(message_text, "plain")) with smtplib.SMTP_SSL(ACCOUNT_SMTP, ACCOUNT_PORT, context=context) as server: server.login(ACCOUNT, ACCOUNT_PASSWD) server.sendmail(ACCOUNT, FOWARD_EMAIL, message.as_string()) print("Sent email")
With all those bits, a text-to-email forwarding system looks something like this. You can rearrange this script into an auto-reply system or proper texting app or slowly transfer whatever data you want over a very slow data pipe.
Model | Status | Lead Time | ||
---|---|---|---|---|
Librem Key (Made in USA) | In Stock ($59+) | 10 business days | ||
Librem 5 | In Stock ($699+) 3GB/32GB | 10 business days | ||
Librem 5 COMSEC Bundle | In Stock ($1299+) Qty 2; 3GB/32GB | 10 business days | ||
Liberty Phone (Made in USA Electronics) | Backorder ($1,999+) 4GB/128GB | Estimated fulfillment early November | ||
Librem 5 + SIMple (3 GB Data) | In Stock ($99/mo) | 10 business days | ||
Librem 5 + SIMple Plus (5 GB Data) | In Stock ($129/mo) | 10 business days | ||
Librem 5 + AweSIM (Unlimited Data) | In Stock ($169/mo) | 10 business days | ||
Librem 11 | In Stock ($999+) 8GB/1TB | 10 business days | ||
Librem 14 | Backorder ($1,370+) | Estimated fulfillment date pending | ||
Librem Mini | Backorder ($799+) | Estimated fulfillment November | ||
Librem Server | In Stock ($2,999+) | 45 business days |