Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Log speedtest results on Firewalla
# v 0.20
BASEDIR=$(dirname $0)
IFTTTKEY="$(cat $BASEDIR/ | grep IFTTTKEY| cut -f2 -d "=" )"
# set to false if you don't want to log speed to a google Spreadsheet
# Create a webhook with IFTT where:
# event = speedtest
# set a Speedtest server ID. Set to blank to let speedtest choose for you. example below
SERVER="-s 41818"
PARAMS="-f json -p no"
# set to false if you don't want to log speed on your firewalla
# Give your WAN ports names. Leave blank if not used. No spaces in names.
# WAN2="LTE"
if [ ! -f "/data/speedtest" ]; then
echo -e "\n\nspeedcheck not instaleld!\n"
if [ -f "$installer" ]; then
echo Running installer
read -p "Do you want to continue? (y|n) ? " -n 1 -r
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Can't continue without speedtest."
echo "Running installer..."
echo -e "Download,\nrun it to install speedtest and try again."
echo speedtest installed!
now=$(date +"%D %H:%M" | sed -e "s|/|-|g")
if [ -n "$WAN1" ]; then
echo "Testing $WAN1..."
WAN1P=$($speedapp $SERVER -I $eth1 $PARAMS)
WAN1L=$(echo $WAN1P | jq '.ping.latency')
WAN1J=$(echo $WAN1P | jq '.ping.jitter')
WAN1D=$(echo $WAN1P | jq '.download.bandwidth'/125000)
WAN1U=$(echo $WAN1P | jq '.upload.bandwidth'/125000)
WAN1URL=$(echo $WAN1P | jq -r '.result.url')
echo -e "$WAN1 Down: $WAN1D $WAN1 Up: $WAN1U\n\n"
echo "No WAN1"
if [ -n "$WAN2" ]; then
echo "Testing $WAN2..."
WAN2P=$($speedapp $SERVER -I $eth2 $PARAMS)
WAN2L=$(echo $WAN2P | jq '.ping.latency')
WAN2J=$(echo $WAN2P | jq '.ping.jitter')
WAN2D=$(echo $WAN2P | jq '.download.bandwidth'/125000)
WAN2U=$(echo $WAN2P | jq '.upload.bandwidth'/125000)
WAN2URL=$(echo $WAN2P | jq -r '.result.url')
echo -e "$WAN2 Down: $WAN2D $WAN2 Up: $WAN2U\n\n"
echo "No WAN2"
if [ -n "$IFTTTKEY" ] && [ "$GoogleSpreadsheet" = "true" ]; then
if [ -n "$WAN1" ]; then
curl -X POST -H "Content-Type: application/json" -d "{\"value1\": \"$now|||$WAN1|||$WAN1U|||$WAN1D|||$WAN1URL|||$WAN1L|||$WAN1J\"}"$EVENT/with/key/$IFTTTKEY
echo -e "\n\nSkipping WAN1 report..."
if [ -n "$WAN2" ]; then
sleep 10s
curl -X POST -H "Content-Type: application/json" -d "{\"value1\": \"$now|||$WAN2|||$WAN2U|||$WAN2D|||$WAN2URL|||$WAN2L|||$WAN2J\"}"$EVENT/with/key/$IFTTTKEY
echo -e "\n\nSkipping WAN2 report"
echo "IFTTT disabled"
if [ "$LOCALLOG" = "true" ]; then
# Create a log file if it doesn't exist and add a header
HEADER='\"date\",\"ISP\",\"server name\",\"server id\",\"latency\",jitter\",\"packet loss\",\"download\",\"upload\",\"download bytes\",\"upload bytes\",\"share url\"'
if [ ! -f "$log" ]; then
echo -e "\n\nno logfile!\n"
echo $HEADER > $log
echo $WAN1P | tee -a $log
echo $WAN2P | tee -a $log
echo $HEADER > $log.$$
tail -n 2000 $log >> $log.$$
mv $log.$$ $log
echo "Local log disabled"
Copy link

mbierman commented Aug 27, 2021

I simplified the way things are sent to IFTTT. Everything goes into value1 now. If you are a new user, don't worry about this. If you are upgrading, just adjust your IFTTT applet so that value1 is in the first field.

see also this script to install speedtest.

Save this script to /data/

You can run this with a cron job if you like. To do that, go to

cd /home/pi/.firewalla/config/
vi user_crontab

and add a line like this:
0 * * * * timeout -s SIGKILL 5m /data/

that will run the test every hour.

You can choose to send the output to a Google Sheet via IFTTT, to a log file on Firewalla, or both. This supports single or dual WAN firewalla configurations. You can just remove the name of either WAN to disable logging on either WAN if you like. WANs with no name will be skipped.


  • The following will be exported to Google Sheets: Date | ISP | Upload | Download | URL | Latency | Jitter
  • Now the speedtest server is configurable
  • All Speedtest data is saved to the logfile on Firewalla: Date|ISP|Server name|Server id|Latency|Jitter|Packet loss|Downl
    oad|Upload|Download bytes|Upload bytes|Share url

IFTTT setup

Create an applet on IFTTT as follows:
IF web request (choose an event trigger name I use, “FWspeedtest”), THEN add row to google spreadsheet. Put the variable “Value1” in the first field.

You can test the web trigger by going to and click on Documentation and you can test the webhook. Use a few values for value1 like, “date|||ISP||latency|||jitter|||packet loss|||download|||upload|||share url" the , “|||" separates columns. They will appear in the order you list them left to right.

Take the last field after “key” on that page and create a file called, In the /data directory or where you saved this script.

IFTTTKEY=[the value from the key above]

Sample Spreadsheet
Here is a snapshot of the spreadsheet you can copy if you don't want to take the time to figure out how to make the graphs.

Sample Graphs
Download Comparison
Dual Wan Speedtest
LTE speedtest
Sail speedtest
Upload Comparison

Saving to a Google Sheet

Create a shortcut like this with a webhook trigger and a Google Sheet adding a row. Put the ingredient, “value1” in the Google sheet. It is already set up to have all of the columns of data from the Speedtest.


You can also use this script to automatically prune your data so it doesn't exceed IFTTT/Google Maximums. This script must be set to fire anytime the sheet is modified

function acraCleanup() {
  const ss = SpreadsheetApp.getActive();
  const sheet = ss.getSheetByName('Sheet1');
  const maxNumDataRows = 1994;
  const numHeaderRows = sheet.getFrozenRows() || 1;
  const numRows = sheet.getMaxRows();
  const numRowsToDelete = numRows - numHeaderRows - maxNumDataRows;
  if (numRowsToDelete <= 0) {
    console.log(`🛑 Nothing to do: sheet '${sheet.getName()}' has ${numRows} rows, which is less than or equal to the allowed max of '${numHeaderRows + maxNumDataRows}'.`);
  } else {
  const rowStartKeep = numHeaderRows + 1 + numRowsToDelete;
  const rangeToKeep = sheet.getRange('A' + rowStartKeep + ':Z');
  const target = sheet.getRange('A' + (numHeaderRows + 1));
  const rowStartDelete = numHeaderRows + 1 + maxNumDataRows;
  sheet.deleteRows(rowStartDelete, numRowsToDelete);
  console.log(`✂️ Pruned ${numRowsToDelete} rows at the end of sheet '${sheet.getName()}'.`);

Copy link

cstrat commented Aug 29, 2021

Champion thank you for sharing!
I ended up just using a node script for mine as I am much more familiar with that than bash.

One issue I had was inbound NAT only seems to work on the external interface. Had to do some juggling with HTTP headers otherwise my nginx proxy wasn't working with the firewalla post.

I was wondering if the Firewalla resets cron on reboot?
I'd also never seen timeout in the cron, but that makes sense with speediest, stop it getting stuck for too long.

Copy link

mbierman commented Aug 30, 2021

@cstrat if you edit the cron normally, changes may be lost at updates and maybe reboots? But if you follow the instructions above Firewalla will append the file above with crontab and it does persist.

Copy link

cstrat commented Aug 30, 2021

Is there a step missing with the cron or do I need to reboot the Firewalla for it to pickup that user_crontab file?

Copy link

mbierman commented Aug 30, 2021

@cstrat yeah, edit the file and reboot. Firewalla will then add your cron entries to the system and they will stay in place until you edit or remove the file and reboot again.

Copy link

cstrat commented Aug 30, 2021

Awesome, thanks! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment