Skip to content

Instantly share code, notes, and snippets.

@mbierman
Last active January 25, 2024 14:26
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mbierman/6a32df2909202c373a6a39063181dc40 to your computer and use it in GitHub Desktop.
Save mbierman/6a32df2909202c373a6a39063181dc40 to your computer and use it in GitHub Desktop.
Log speedtest results on Firewalla
#!/bin/bash
# https://gist.github.com/mbierman/6a32df2909202c373a6a39063181dc40
# v 0.4.1
BASEDIR=$(dirname $0)
IFTTTKEY="$(cat $BASEDIR/IFTTT.data | grep IFTTTKEY| cut -f2 -d "=" )"
EVENT="FWspeedtest"
log=/data/logs/logspeed.log
# set to false if you don't want to log speed to a google Spreadsheet
GoogleSpreadsheet=true
# 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=""
# SERVER="-s 49365"
PARAMS="-f json -p no"
# set to false if you don't want to log speed on your firewalla
LOCALLOG=true
# Give your WAN ports names. Leave blank if not used. No spaces in names.
WAN1="True"
# WAN2="LTE"
eth1=eth0
eth2=eth1
if [ "$(command -v /home/pi/ookla-speedtest/speedtest)" ]; then
speedapp=/home/pi/ookla-speedtest/speedtest
else
if [ ! -f "/data/speedtest" ]; then
echo -e "\n\nspeedcheck not instaleld!\n"
installer="/home/pi/.firewalla/config/post_main.d/install_speedtest.sh"
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."
exit
else
echo "Running installer..."
/home/pi/.firewalla/config/post_main.d/install_speedtest.sh
fi
else
echo -e "Download https://gist.github.com/mbierman/9ac6a35622ee5a0c631ed6f6ad74b722,\nrun it to install speedtest and try again."
exit
fi
else
echo speedtest installed!
speedapp=/data/speedtest
fi
fi
# Uncomment the line below to override using the default Firewalla speedtest and use the latest speedtest (which you installed) using the script at https://gist.github.com/mbierman/9ac6a35622ee5a0c631ed6f6ad74b722
# speedapp=/data/speedtest
echo using $speedapp
now=$(date +"%D %H:%M" | sed -e "s|/|-|g")
if [ -n "$WAN1" ]; then
echo -n "Checking WAN1..."
WAN1P=$($speedapp $SERVER -I $eth1 $PARAMS)
WAN1ISP=$(echo $WAN1P | jq -r '.isp')
echo "Testing $WAN1ISP..."
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)
WAN1SN=$(echo $WAN1P | jq -r '.server.name')
WAN1SID=$(echo $WAN1P | jq -r '.server.id')
WAN1URL=$(echo $WAN1P | jq -r '.result.url')
WAN1PORT=$(echo $WAN1P | jq -r '.interface.name')
WAN1P="$now,${WAN1ISP},${WAN1L},${WAN1J},${WAN1D},${WAN1U},${WAN1URL},${WAN1SN},${WAN1ID},${WAN1PORT}"
echo -e "$WAN1ISP Down: $WAN1D $WAN1ISP Up: $WAN1U\n\n$WAN1SN ($WAN1ID)"
else
echo "No WAN1"
fi
if [ -n "$WAN2" ]; then
echo "Testing WAN2..."
WAN2P=$($speedapp $SERVER -I $eth2 $PARAMS)
WAN2ISP=$(echo $WAN2P | jq -r '.isp')
echo "Testing $WAN2ISP..."
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)
WAN2SN=$(echo $WAN2P | jq -r '.server.name')
WAN2SID=$(echo $WAN2P | jq -r '.server.id')
WAN2URL=$(echo $WAN2P | jq -r '.result.url')
WAN2PORT=$(echo $WAN1P | jq -r '.interface.name')
WAN2P="$now,${WAN2ISP},${WAN2L},${WAN2J},${WAN2D},${WAN2U},${WAN2URL},${WAN2SN},${WAN2ID},${WAN2PORT}"
echo -e "$WAN2ISP Down: $WAN2D $WAN2ISP Up: $WAN2U\n\n$WAN2SN ($WAN2ID)"
else
echo "No WAN2"
fi
if [ -n "$IFTTTKEY" ] && [ "$GoogleSpreadsheet" = "true" ]; then
if [ -n "$WAN1" ]; then
curl -X POST -H "Content-Type: application/json" -d "{\"value1\": \"$now|||$WAN1ISP|||$WAN1U|||$WAN1D|||$WAN1URL|||$WAN1L|||$WAN1J|||$WAN1SN|||$WAN1SID|||$WAN1PORT\"}" https://maker.ifttt.com/trigger/$EVENT/with/key/$IFTTTKEY
else
echo -e "\n\nSkipping WAN1 report..."
fi
if [ -n "$WAN2" ]; then
sleep 10s
curl -X POST -H "Content-Type: application/json" -d "{\"value1\": \"$now|||$WAN2ISP|||$WAN2U|||$WAN2D|||$WAN2URL|||$WAN2L|||$WAN2J|||$WAN2SN|||$WAN2SID|||$WAN2PORT\"}" https://maker.ifttt.com/trigger/$EVENT/with/key/$IFTTTKEY
else
echo -e "\n\nSkipping WAN2 report"
fi
else
echo "IFTTT disabled"
fi
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
fi
echo $WAN1P | tee -a $log
echo $WAN2P | tee -a $log
exit
echo $HEADER > $log.$$
tail -n 2000 $log >> $log.$$
mv $log.$$ $log
else
echo "Local log disabled"
fi
@mbierman
Copy link
Author

mbierman commented Aug 27, 2021

Update 2:
Note, Firewalla now includes the speedtest.net binary. However, it is several versions older than is available from Speedtest so the script looks to see if in fact Firewalla has speedtest installed and is happy if it does. If not, it will install the latest speedtest for you. If you would rather use the latest speedtest, uncomment line specified and install speedtest using the script mentioned. This will simply ignore Firewalla's speedtest and use the newer one.

Update 1:
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/logspeed.sh.

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/logspeed.sh

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.

Updates

  • 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 https://ifttt.com/maker_webhooks 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, IFTTT.data 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.

39B1A7F7-5475-469C-94DE-E0C271FF9B96
1AFA6CEE-2385-4B8E-AD23-AABAD8712401

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}'.`);
    return;
  } else {
  const rowStartKeep = numHeaderRows + 1 + numRowsToDelete;
  const rangeToKeep = sheet.getRange('A' + rowStartKeep + ':Z');
  const target = sheet.getRange('A' + (numHeaderRows + 1));
  rangeToKeep.copyTo(target);
  const rowStartDelete = numHeaderRows + 1 + maxNumDataRows;
  sheet.deleteRows(rowStartDelete, numRowsToDelete);
  console.log(`✂️ Pruned ${numRowsToDelete} rows at the end of sheet '${sheet.getName()}'.`);
}
}

@cstrat
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.

@mbierman
Copy link
Author

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.

@cstrat
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?

@mbierman
Copy link
Author

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.

@cstrat
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