Writing Your Own Tasks

A task is a shell script with a few header lines at the top. It shows its progress on the device screen through a small set of TASK_ functions, and the same script still works when you run it from a terminal over SSH.

ConnectCopy files with Secure File Transfer, or work on the device over Secure Shell.
Add a scriptSave it in /opt/muos/share/task, shown as Internal/share/task over Secure File Transfer, or in a folder inside it. The file name, without .sh, is the name in the menu.
Make it runnableRun chmod +x on the script, or the device cannot start it.
Try itOpen Applications > Task Toolkit and run it. Folders only appear once they hold a script.

A first task

#!/bin/sh
# HELP: Counts the files in your ROMS folder
# ICON: storage
# EXECUTION_MODE: progress
# CAN_CANCEL: 0
# PROTOCOL_VERSION: 1

. /opt/muos/script/var/func.sh
. /opt/muos/script/var/ui.sh

TASK_BEGIN "count_roms" "Count ROMs"

ROMS="$(GET_VAR "device" "storage/rom/mount")/ROMS"
TASK_STATUS "Counting files in $ROMS"

COUNT=$(find "$ROMS" -type f 2>/dev/null | wc -l)
TASK_DETAIL "$COUNT files found"

TASK_COMPLETE "Found $COUNT files"

func.sh brings in the MustardOS settings helpers, and ui.sh brings in the TASK_ functions. Source both, in that order, at the top of every task.

Header lines

The frontend reads these comment lines from the top of the script. Each is written as # KEY: value.

Header Values What it does
HELP Text Shown when you press Menu on the task.
ICON A glyph name The glyph beside the task, such as clear, diagnostic, junk, network, storage or theme.
EXECUTION_MODE progress, prompt or terminal How the task runs. Without this line the task opens a terminal.
CAN_CANCEL 0 or 1 Whether B can stop the task while it runs.
NEVER_CANCEL 1 Refuses cancelling even where CAN_CANCEL allows it. Use it for work that must not stop halfway.
PROTOCOL_VERSION 1 The version of the task functions the script was written for.
progressShows a progress screen and reads the TASK_ functions. Best for most tasks.
promptThe same as progress, for tasks that ask questions with TASK_PROMPT.
terminalOpens a terminal and shows everything the script prints. Use it when the output itself is the point, such as a live input test.

Task functions

Every function takes plain text. Tabs and line breaks are removed, so keep each message to one line.

TASK_BEGIN id titleStarts the task. The id is a short name with no spaces, and the title is shown at the top of the progress screen.
TASK_STATUS textThe main line saying what the task is doing now.
TASK_DETAIL textA smaller second line, such as a file name or a count.
TASK_PROGRESS value maxFills the progress bar, for example TASK_PROGRESS 3 10. A max of 0 shows a moving bar for work of unknown length.
TASK_LOG level textWrites a line to the log without showing it, with a level such as info or warn.
TASK_ERROR code textReports a failure. The code is a short name for the log and the text is shown to the person using the device.
TASK_COMPLETE textFinishes the task and shows the closing message.
TASK_PROMPT ...Asks a question and gives back the answer. See Asking a question, below.
TASK_IS_NATIVETrue when the task is running on the progress screen, and false in a terminal or over SSH.

A task finishes well when it calls TASK_COMPLETE and exits with 0. Calling TASK_ERROR or exiting with any other number shows the task as failed.

Asking a question

TASK_PROMPT shows a dialogue on the device and prints the option that was chosen. Set EXECUTION_MODE to prompt for tasks that use it.

CHOICE=$(TASK_PROMPT "clear_saves" "confirm" "Clear Saves" \
	"Remove every save file in this folder?" "Clear|Cancel" "Cancel") || CHOICE="Cancel"

if [ "$CHOICE" != "Clear" ]; then
	TASK_COMPLETE "Nothing was changed"
	exit 0
fi
idA short name for the question.
typeconfirm for a yes or no style question, or choice to pick from a list.
titleThe heading of the dialogue.
messageThe question itself.
optionsThe answers, separated by |, such as Low|Medium|High.
defaultThe answer selected when the dialogue opens, and the answer given when the task runs outside the frontend.

Pressing B on a question picks the last option, so always put the safe answer, such as Cancel, last. If the task is cancelled while a question is open, TASK_PROMPT returns a non-zero status, which is why the example falls back to Cancel.

Cancelling

With CAN_CANCEL: 1, pressing B while the task runs asks it to stop. After a second and a half the task receives TERM, and after three seconds it is stopped outright. Catch TERM to tidy up anything left half finished.

TEMP="$MUOS_RUN_DIR/my_task.tmp"
trap 'rm -f "$TEMP"; exit 1' TERM

# long running work that writes to $TEMP

Rules for task scripts

POSIX shTasks run in BusyBox sh, so avoid features that only bash has, such as [[ ]] and arrays.
Descriptor 3The task functions talk to the frontend through descriptor 3. Never close or redirect it.
Plain outputIn progress and prompt modes, ordinary echo output is not shown. Use TASK_STATUS or TASK_DETAIL for anything the user should see.
ErrorsAnything written to stderr goes to the log only.
Unique namesGive your tasks names that the built-in tasks do not use, so an update cannot replace them.
Keep a copyA fresh install replaces /opt/muos/share/task, so keep your own tasks somewhere safe as well.

Useful helpers

These come from func.sh and are used throughout the built-in tasks.

GET_VAR area keyReads a setting. Areas are config, device, kiosk and system, for example GET_VAR "device" "storage/rom/mount".
SET_VAR area key valueChanges a setting, for example SET_VAR "config" "settings/general/orientation" 0.
DELETE_CRUFT folderRemoves macOS and Windows junk files from a folder.
LOG_INFO "$0" 0 tag textWrites to the MustardOS log when debug logging is on. LOG_WARN, LOG_ERROR and LOG_DEBUG work the same way.
$MUOS_STORE_DIRThe MustardOS storage folder, which holds the catalogue, history, collections and other user data.
$MUOS_SHARE_DIRThe shared MustardOS folder, which holds emulator files, tasks and other bundled data.
$MUOS_RUN_DIRA folder in memory for temporary files. It is cleared at every restart.

Writing a report

The Diagnostics tasks share a report library. It loads func.sh and ui.sh itself, so it is the only file a report task needs to source. Reports are saved to MUOS/log/diagnostics on SD1.

#!/bin/sh
# HELP: Checks the fan sensor and saves a report to SD1
# ICON: diagnostic
# EXECUTION_MODE: progress
# CAN_CANCEL: 0
# PROTOCOL_VERSION: 1

. /opt/muos/script/var/diag.sh

DIAG_BEGIN "fan_report" "Fan Report"

DIAG_SECTION "Fan"
DIAG_KV "Speed" "$(cat /sys/class/hwmon/hwmon0/fan1_input 2>/dev/null)"
DIAG_CMD "Temperatures" cat /sys/class/thermal/thermal_zone0/temp
[ -e /sys/class/hwmon/hwmon0 ] || DIAG_FINDING "FAN" "No fan sensor was found."

DIAG_FINISH "Fan report saved"
DIAG_BEGIN id titleStarts the task and the report, and records the board, version and kernel.
DIAG_SECTION nameStarts a new section in the report and shows it as the status.
DIAG_KV label valueWrites one labelled value.
DIAG_CMD label commandRuns a command for up to 15 seconds and writes its output.
DIAG_FILE pathWrites the contents of a file, or notes that it is missing.
DIAG_FINDING area textAdds a finding to the list at the end of the report.
DIAG_FINISH messageWrites the findings, saves the report and finishes the task.

Testing from a terminal

Run the script over SSH to check it before trying it on the device screen. Outside the frontend the task functions print plain text, and TASK_PROMPT answers with its default.

sh "/opt/muos/share/task/Count ROMs.sh"