DEV Community

Cover image for I Built a Bot to Try and Get Money Back From My Internet Provider
Andrew Healey
Andrew Healey

Posted on • Updated on • Originally published at healeycodes.com

I Built a Bot to Try and Get Money Back From My Internet Provider

In my contract with my ISP, they gave me a range of speeds to expect but also a speed that was the guaranteed minimum. If they aren't able to keep above this minimum, I'm eligible for a discount a few times per year.

I didn't want to sit spamming speedtest.net to check when my speeds were getting low. I wanted to know if there had been dips. Were the dips at the same times? Was there a pattern? I needed data and some data visualization to know if/when I might be eligible for any discounts and whether it was worth pursuing.

Something like this

Bandwidth graph

Raspberry Pi

Running on my trusty Raspberry Pi. Powered via a USB cable from the router and connected via ethernet so the results (barring other network use) should be reliable.

Check out my current bandwidth results live or read the source code on GitHub.

Over-designed

I started this project off way too complicated. I used a web browser automation framework called Selenium to visit Netflix's fast.com bandwidth test to check my speed. It used a headless Chromium instance.

# wait for test to finish i.e. when 'your speed message' is shown
WebDriverWait(driver, 120).until(
    expected_conditions.presence_of_element_located(
        (By.CSS_SELECTOR, FAST_COMPLETE_CSS))
)
Enter fullscreen mode Exit fullscreen mode

Not only was this quite brittle and vulnerable to CSS changes (which had to be hardcoded, see above) it also meant that any users of this project needed to get headless Chromium setup for Raspbian (the default Linux distro for Raspberry Pi). After troubleshooting this and getting it running, I realized it was over-designed.

Using this method remains an option in the project (browsertest.py) but it gives less accurate results on Raspberry Pis due to the browser overhead. Note: this project works on any platform but is mainly targetted towards Pis.

I discovered late in the game that speedtest.net have a great CLI library and I should have used it from the start. Here's how I get the download speed in clitest.py.

import speedtest

# ..

def get_speed():
    """
    Use Speedtest CLI to test bandwidth speed.
        :return: Download speed in Mbps
    """
    s = speedtest.Speedtest()
    s.download()
    results_dict = s.results.dict()
    return results_dict['download'] / 1048576  # convert bits to megabits
Enter fullscreen mode Exit fullscreen mode

This script runs via crontab and is pointed at the server. A command similar to python clitest.py 'https://server-location/save' 'password' runs every half an hour. The Windows alternative is Task Scheduler but I believe it's clunkier.

Back-end

I thought it would be neat to be able to check my speeds from anywhere so I created a Glitch project to store, receive, and host the results. It's an Express/Node project that has two API routes.

Results are sent to /save along with a password, which is set via an environmental variable on Glitch.

A slice of results can be read from /read in JSON. Here's what the Express route for that looks like.

// get bandwidth test results for graphing here
app.get("/read", function(request, response) {
  const data = db.get("results").value();
  const prepared = data.map(s => {
    return { x: s.date, y: Number(s.speed).toFixed(3) };
  });
  const trimmed = prepared.slice(Math.max(prepared.length - 48, 1));
  response.send(trimmed); // send a slice of results
});
Enter fullscreen mode Exit fullscreen mode

For storage, I wanted something that didn't require any setup at all. lowdb is a small local JSON database and it's perfect because there's only ever going to be one process reading or writing, and the write event occurs every half an hour or so. lowdb creates the 'database' file if it doesn't already exist.

Data visualization

Chart.js is the go-to library for graphs in JavaScript and uses the Canvas API. It's batteries-included and looks nice by default (but perhaps I'm just used to the style!). It's about fifty lines, including the API call.

fetch('/read')
    .then(response => response.json())
    .then(json => renderGraph(json));
const safeDate = time => new Date(parseInt(time)).toUTCString();
const renderGraph = (speedData) => {
    var ctx = document.getElementById('myChart').getContext('2d');
    var myChart = new Chart(ctx, {
    type: 'scatter',
    data: {
        datasets: [{
            data: speedData,
            backgroundColor: () => 'rgba(255, 99, 132, 0.2)',
            borderColor: () => 'rgba(255, 99, 132, 1)',
            borderWidth: 1,
            pointRadius: 5,
        }]
    },
    options: {
        scales: {
            xAxes: [{
                type: 'linear',
                position: 'bottom',
                ticks: {
                    userCallback: (label, index, labels) => safeDate(label)
                },
                scaleLabel: {
                    display: true,
                    labelString: 'Date'
                }
            }],
            yAxes: [{
                scaleLabel: {
                    display: true,
                    labelString: 'Mbps'
                },
                ticks: {
                    beginAtZero: true
                }
            }],
        },
        title: {
            display: true,
            text: 'Bandwidth Test Results'
        },
        legend: {
            display: false,
        },
        tooltips: {
            callbacks: {
                label: function(tooltipItem, data) {
                return `${tooltipItem.value} Mbps @ ${safeDate(tooltipItem.label)}`;
                }
            }
        }
    }
    });
}
Enter fullscreen mode Exit fullscreen mode

I find it easy to play around with Chart.js and like JavaScript it's very productive if you want to throw something together. The docs are great and it's a large enough library that common searches will find helpful StackOverflow answers.

Where to go from here

So far my speeds have been hovering around the guaranteed minimum, usually dipping just under around 8pm local time. At current, I have no reason to complain! A pleasant surprise.

It will be straightforward to diagnose any future bandwidth problems as I now have an up-to-date record of my speeds. Hopefully, some of this project's code will help you diagnose any simple problems with your bandwidth throughout the day.


Join 150+ people signed up to my newsletter on programming and personal growth!

I tweet about tech @healeycodes.

Discussion (38)

Collapse
adam_cyclones profile image
Adam Crockett

Now we need some AI to read contracts and look for sweet bits of info like this!

Collapse
healeycodes profile image
Andrew Healey Author

Automate all the things! 😊

Collapse
adam_cyclones profile image
Adam Crockett

Absolutely! But actually a contract reading AI would be fantasticly useful and I would pay for that sort of service.

Thread Thread
phlickey profile image
Phil

This would be tremendously empowering for marginalized communities that have historically received the short end of the stick when it comes to Ts&Cs. Could potentially be a powerful tool to democratize the legaleese.

Thread Thread
adam_cyclones profile image
Adam Crockett

I love this angle Phil, really interesting! It's so tempting to go away and build this, if anyone has a crack I'd love to checkout your work. I could imagine you would ask the AI for positive or negative traits within a document. How could this AI become a consumer product, some sort of Phone app. Perhaps it's more of an API like Amazon lex powers poly to make "Alexa".

Collapse
sarafian profile image
Alex Sarafian

I'm wonder how is the minimum guaranteed and compensated when your speed test depends on a 3rd party. The only way to 100% now what is your speed is to download/upload files from your ISPs servers and that is also not 100% concrete because the network could be fine but the host not. In other words, even if you found the dips manually or automated, doesn't your ISP have the best "get out of jail" free card in the world?

I'm not trying to undermine your effort I'm just wondering how does your ISP define minimum speed.

Collapse
healeycodes profile image
Andrew Healey Author

Hi Alex, that’s a great question.

I went and scanned the terms and conditions last night and the ISP have their own private speed test for their customers. It takes longer and is presumably logged on their end.

The result of their test matches the graph’s results.

My intentions with this project is for the graph to show me if I have any issues, and at what times, so that I can go and follow up in a more official manner.

I agree that they would not accept this graph as evidence.

Collapse
phlickey profile image
Phil

This was niggling at me. Thanks for clarifying, and congratulations on a very cool project.

Collapse
sarafian profile image
Alex Sarafian

Admirable. If I understand correctly, does your isp publish their speed test results? Which country and ISP if in may ask? Is it a national regulation?

Collapse
clairmont32 profile image
Matthew Clairmont • Edited

I've thought about doing this with Fios but never followed through. I got the idea from my first IT job where we requested refunds due to outages or low bandwidth supported by a Nagios graph. Did you have any positive results from reporting the slowness to your ISP? Getting a few bucks back off of them would be a good win and worth the setup time if you expanded it to email them with each dip below minimums.

Collapse
healeycodes profile image
Andrew Healey Author

It turns out that there weren't any problems with my bandwidth so I've had no contact.

If the graph showed a pattern, I would perform any official ISP speed-tests at the now-predictable slow parts.

I like that automation idea though. Perhaps a Twitter bot!

Collapse
clairmont32 profile image
Matthew Clairmont

I like it!! Twitter is usually a good way to get the fast attention of customer support reps from bigger companies!

Collapse
thomasghenry profile image
Thomas G Henry

Nice! Is it worth being more unpredictable from their perspective? I might vary the interval or even just offset it a bit to come out of phase with itself day after day (every 29 mins instead of 30, let's say), as well as stick a VPN in there to ensure the ISP is not prioritizing speed test traffic. Varying the speed test host might not be a bad idea either if you can find others.

Collapse
healeycodes profile image
Andrew Healey Author

Every 29 minutes is a great idea!

Quite a few interesting ideas here. I know that speedtest-cli accepts --list which displays a list of speedtest.net servers sorted by distance. Presumably, there are further configuration options.

There are a few ways to make this project more real-world and reliable.

Thanks for the insightful comment.

Collapse
sameerul97 profile image
Sameerul

Inspiring post, i have Raspberry Pi lying in my home. Going to implement this project. I also live in UK but which ISP provides this discount deal, from my experience i haven't heard about ISP offering discounts if the speed limit isn't consistent. Would love to get some discount since my internet tends to be slow quiet regularly.

Collapse
healeycodes profile image
Andrew Healey Author

My ISP requires you to run their speed test in order to be eligible for the discount. But this project might tell you at which times to run it.

My graph tends to dip in the afternoon until morning.

Let me know if you have any issues with the project! 😁

Collapse
martinwheeler profile image
Martin Wheeler

Hey Andrew, great article and great work on the tool, I'll have to get something similar setup soon! Just a couple of questions:

  1. Does this cause any dips in network speed when the test is running? I.e. do you feel the test running when you are browsing or doing other things on the internet, or is the test pretty quick to finish?
  2. Why did you opt for 30 minute intervals?
Collapse
healeycodes profile image
Andrew Healey Author

Either test doesn’t affect browsing, I've been playing games that rely on low latency and haven’t seen any spikes. On very slow connections (~5Mbps? Perhaps the test would be felt).

I felt that any speed issues that lasted shorter than half an hour would be β€˜blips’ and I wouldn’t mind missing them. I haven’t put too much thought into the 30 minute timing interval.

Thanks for your questions! 😊

Collapse
asparallel profile image
AsParallel • Edited

Interesting, do the UK ISPs consider third party data valid for the discount claims? I know ATT, Comcast and TW have a similar scheme in the US, but you have to provide results using the speed test mechanism on their website or built in to the router/gateway.

Either way, very cool project, thanks for sharing :)

Collapse
healeycodes profile image
Andrew Healey Author

I mentioned this in another comment, that the aim of this project is to serve as a kind of β€˜bandwidth canary’ so that more official complaint steps can be taken.

I thought about automating the process with just my ISP but wanted to provide a more general tool 😊

Thanks!

Collapse
hephalump profile image
Jason S

This is the best thing I've read all day.

Collapse
healeycodes profile image
Andrew Healey Author

Very! Right down to the nice engineer who hooked up our line.

Here in the UK, ISPs tend to be quite customer-oriented (in my experience).

Collapse
fultonbrowne profile image
Fulton Browne

I know what I am doing saturday! :]

Collapse
banidrum profile image
Daniel Brum

That is a great idea, pretty cool.

Collapse
healeycodes profile image
Andrew Healey Author

Thanks Daniel!

Collapse
joflixo profile image
Joflix Ooko

This is cool man,

Collapse
stiakov profile image
Santiago Torres

Great! I'm writing my own in NodeJS.
I'll post my results here.

Collapse
stiakov profile image
Santiago Torres • Edited

Thank you @healycodes, very inspiring! πŸš€.

Here is the result, it's enhanced with a telegram bot, and send relevant notifications about changes in data points.
Bandwidth Checker and Logger built with NodeJS

Collapse
codelitically_incorrect profile image
codelitically_incorrect

this man is an engineer.

Collapse
libertxyz profile image
Libert S

This is so cool! thanks for sharing

Collapse
kamalhm profile image
Kamal

Whoa this is interesting, gonna do this sooner or later

Collapse
vinnymac profile image
Vincent Taverna

Idk if this will help you, but I use this CLI tool written in Go on Linux regularly. It is small and really convenient! github.com/ddo/fast/releases

Collapse
healeycodes profile image
Andrew Healey Author

I’ll check it out! Thanks for sharing it Vincent πŸ‘πŸ»

Collapse
rommik profile image
Roman Mikhailov • Edited

#keepemhonest

Collapse
mpissanos profile image
Manuel Pissanos

Cool!! Nice idea for a side project this weekend. Thanks for sharing...

Collapse
engmms profile image
engmms • Edited

in my country , they will tell you in aggressive way ,How you can debunk our Service ???