FuelRod Locator for Pebble
I like new conveniences. So when the FuelRod kiosks appeared at WDW, promising a lifetime of unlimited LiPo battery swaps for only $30, I really couldn't pass it up. At the time, the ~1000mAh batteries with their 5V1A DC output were nice - I could charge my phone almost continuously at Magic Kingdom, constantly swapping out the battery as they drained. With so many swap locations, there was always one on the path I traversed. The question was: how do you find them?
The kiosks aren't really given much signage in the parks. While the kiosks do have some vinyl wrapping on them to theme them to their particular location, there's nothing alerting you to their presence. They're not on maps, they're not officially listed on a site, nothing.
At least, not any printed maps. Or easily accessible maps.
As it turns out, they're listed on a buried part of the Disney World guest services page - Portable Phone Charger & Battery Purchase Kiosks | Walt Disney World Resort (go.com). That map has every FuelRod Kiosk listed at WDW, with GPS coordinates, too. A similar one exists for Disneyland, too - Portable Phone Charging Kiosks | Disneyland Resort (go.com).
How can I make this more convenient? How about a Pebble app that automatically tells you which location is closest based upon your phone's GPS location?
Scraping the Site
The first challenge is getting the information from the site. Disney's websites, as made obvious by any use of them, are not particularly light. Nor static. As it turns out, the page's information actually loads after the site does. So a simple HTTP request won't do - attempting to do so would fetch the empty website and the javascript required to load everything. Not viable. So, I turned to piloting a web browser.
Selenium is a system used to "drive" a web browser to perform specific actions. It's typically used for test/automation, but also occasionally for purposes like this. So, I figured out how to open an instance of Google Chrome driven by Selenium, pointed it at the page, and... had to get more complicated. As mentioned before, the webpage doesn't actually load immediately - there's some extra loading that happens thanks to some weird way they build their pages. Therefore, I have to make Selenium wait until the content I'm actually looking for appears.
How do I find the content?
That's done using a system called "XPATH" - a way to find or refer to objects in a DOM/markup environment. You can actually copy an XPATH for HTML elements in chrome's debugger - right click on an element, go to "copy," and select "XPath." I did some tweaks to that, and was able to get all the information about the points of interest. Finding out the map was actually in an iframe helped too - I could just navigate to the webpage that actually hosted the whole map, and avoid trying to load the entire disney site.
Parse everything, package it up into a JSON file, and... now what?
Hosting the Data
I already was paying for / had set up a system using Python and Flask on Pythonanywhere for Liftoff - so I thought it would make sense to host the data on there. For some reason, I really wanted to keep the scraping and hosting separate, and also scrape for new data every day.
As if WDW ever was going to change the position of these things on a daily basis.
Regardless, I decided that the scraping was going to be hosted on my desktop PC and run every day at about 1am. I found out a way for it to run headless (without visually opening a web browser), automated by the Windows Task Scheduler, and could get data every day. But how do I get the data from my computer to the web server?
I could set up some kind of FTP/SSH session to upload it. In fact, that would be the correct choice, retrospectively. But I instead decided to have an endpoint where I could just POST the file to.
Naturally, that comes with a bunch of security concerns. What if someone wanted to upload some bad data to the site? In the astronomically tiny case that someone would (1) find the endpoint (2) figure out how to take control of my service through a JSON file upload and (3) have any benefit from doing so, I implemented a little system: Signatures. My local desktop had a private key, the server a public key, and the local desktop would attempt to create a signature from the data and private key. The server would decode it with the public key, verify it, and if it was appropriate, upload it.
Far more complicated than it needed to be. But it was what I came up with at the time.
The actual hosting of the data was simple - I'd upload a single JSON file with data for both Disneyland / WDW, and when requested (at either the Disneyland/Disneyworld URL, or the ALL url) return the relevant data. It was neat.
The Pebble App
Now for the easiest part. With the API created and configured, I could crank out the pebble app in a day. When the app was opened:
- I'd send a message over bluetooth to the JS component of the pebble app (Pebble apps have a watch-side component written in C, and a JS component on the phone to perform actions over internet / with phone data) which would issue the web request
- Compare the GPS coordinates of the phone to the GPS coordinates in the list
- Pick the closest location and send its name to the watchapp
- Watchapp would update the text display to the name of the closest location.
I had aspirations of including some kind of "magic compass" functionality, where the watchapp would use the device's compass functionality to point the user in the direction of the kiosk. At the time, my grasp of math was a bit less than what it is now, so I couldn't figure out how to do it on paper.
After creating the app, I prepared some screenshots and media for the Pebble store, which I've preserved below.
Chalk Platform (Pebble Time Round) |
Aplite/Diorite Platform (Pebble Classic / Pebble 2) |
Basalt Platform (Pebble Time) |
With the API built, I decided I could easily adapt this to other platforms. So I did. Or, at least, I did on Android...
You can find the FuelRod Locator on the Pebble Appstore here: https://apps.rebble.io/en_US/application/5939fe5d0dfc32af610003df
Though it is no longer functional since I sunset the service that hosted the location data after about five years of operation.
You can find the source code for the fuelRodScraper here: thomasstoeckert/fuelRodScraper: The location scraper for fuelRodLocator (github.com)