7

Updating My "Get On The Beers" Indicator To Track Vaccinations

 3 years ago
source link: https://dev.to/mitchpommers/updating-my-get-on-the-beers-indicator-to-track-vaccinations-495b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Over 6 months have passed since I first made this indicator light, and while it was good during previous outbreaks, the increasing availability of vaccinations in Australia means runs of 0 cases is less important. More important is getting jabbed! So I updated my light to track daily jabs!

The New Colour Code:

  • No data yet: White
  • Under 25,000 jabs in NSW today: Red
  • Under 80,000 jabs: Blue
  • Under 80,000 jabs: Green
  • 100,000 plus jabs: Party Mode!

Finding The Data

Just like the original build, finding a data source to use was critical to making it work. Data NSW didn't have vaccination data available that I could find and I didn't want to go down the path of searching for different API's hoping that one would have the data I wanted.

The COVID LIVE website has a page with NSW Daily vaccinations on it in table form! There's also a nice JSON file with all the data that it's creator Migga told me about when he came across my indicator light the first time. The Electric Imp agent has a 2048kB memory limit though, so I can't load the full JSON file in. The page with the table on it will have to do!

A screenshot of the COVID LIVE website showing the latest vaccination numbers for NSW.

The source code of the website makes it easy to extract the number I want to make the light colour decision on. I need to find the last column in the first table row (not table header) and understand that number.

The source code for the COVID LIVE website showing the table of daily vaccinations that I am interested in.

Making It Light Up

I used the code from the previous version that tracked daily cases of known local transmission as the starting point. It has code on the device to set the lights colour based on the number it received. And the agent has code to make a web request and massage data in the response to determine the number and send it oto the device.

I started with the device code (code that runs on the hardware), changing the numbers for each colour. Then I reversed the order of the logic. This meant it would check if it was going to be party mode first, then work back towards 0 for red, and finally treat any other response (i.e. negative numbers) as an indication to show white. The main driver behind reversing the order of the code was to make it so I only needed to disable party mode once, instead of disabling it for each colour. Once done this was the device code:

// Configure the LEDs #require "WS2812.class.nut:3.0.0" spi <- hardware.spi257; pixels <- WS2812(spi, 1); hardware.pin1.configure(DIGITAL_OUT, 1);

function updateLight(jabsAdministered) { server.log("Update Recieved: " + jabsAdministered + " Jabs Administered"); if (jabsAdministered >= 100000) { if (!rainbowMode) { // Only enable rainbow mode once server.log("Starting rainbow mode"); rainbowMode = true; // Enable rainbow mode rainbow(); // Start the rainbow loop } } else { rainbowMode = false; // Ensure rainbow mode is disabled if (jabsAdministered >= 80000) { pixels.set(0, [0, 255, 0]).draw(); // Green } else if (jabsAdministered >= 25000) { pixels.set(0, [0, 0, 255]).draw(); // Blue } else if (jabsAdministered >= 0) { pixels.set(0, [255, 0, 0]).draw(); // Red } else { // Something has likely gone wrong default to white pixels.set(0, [255, 255, 255]).draw(); // White } } }

// Used for making the light change colours through a rainbow! rainbowMode <- false; // If set to false, rainbow mode will do nothing rainbowStep <- 0; // The current step in the colour wheel function rainbow() { if (rainbowMode) { local colour; // This determines the colour the LED should be set to based on the current step if(rainbowStep < 85) { colour = [rainbowStep * 3, 255 - (rainbowStep * 3), 0]; } else if(rainbowStep < 170) { local diff = rainbowStep - 85; colour = [255 - (diff * 3), 0, diff * 3]; } else { local diff = rainbowStep - 170; colour = [0, diff * 3, 255 - (diff * 3)]; } pixels.set(0, colour).draw(); // Set the LED colour rainbowStep += 5; // Incriment the step count rainbowStep = rainbowStep % 256; // Ensure we cycle back to 0 once we reach the end imp.wakeup(0.05, rainbow); // Briefly wait and then change colours again } }

// Call this function when we want to ask for updated numbers function requestUpdate() { agent.send("requestUpdate", null); imp.wakeup(1800, requestUpdate); // After 1800 seconds, request another update }

server.log("Device Started"); agent.on("update", updateLight); // when we recieve new numbers update the light requestUpdate(); // Request our first update

Once I had the device code updated (and had tested it by feeding it some hardcoded data) I moved on to writing the agent code (code that runs in the cloud).

I renamed variables and functions to represent what the code would now be doing, then started writing code to parse the HTML that I was extracting the data from. To extract the data I:

  • Found the first line of the table rows
  • Found the end of the opening tag for the net change column
  • Removed everything prior to that
  • Found the first closing tag (which could either be the a span or a td tag)
  • Removed it and everything after it
  • Checked if there was still an opening tag that needed removing
  • Removed it if there was
  • Checked if all I had left was a "-" character (indicating that there was no data for the day yet)
  • If there wasn't, removed all commas from the string and turned it into an integer

I tried thinking of other ways I could simplify the code for extracting this data, but there weren't any libraries or functions I could see that would help. This was the agent code once I was finished:

dataUrl <- "https://covidlive.com.au/report/daily-vaccinations/nsw"; // COVID LIVE is CC BY 4.0 https://creativecommons.org/licenses/by/4.0/

// Function that will run when the device requests an update function updateDevice(data) { server.log("Update Requested."); local jabs = jabsAdministeredToday(); device.send("update", jabs); }

function jabsAdministeredToday() { local request = http.get(dataUrl); local response = request.sendsync(); server.log("Response Status: " + response.statuscode); if (response.statuscode != 200) { server.log("Error: " + response.body); return -1; } local lines = split(response.body, "\r\n"); foreach (line in lines) { // Uncomment the next line to test zero jabs administered // line = "<tr class=\"odd\"><td class=\"COL1 DATE\">16 Feb <span>21</span></td><td class=\"COL2 DOSES\">0</td><td class=\"COL3 VAR\"><span class=\"zero\"> </span></td><td class=\"COL4 NET\"><span>0</span></td></tr>"; if (line.find("<tr ") == 0) { server.log(line); // <tr class="even"><td class="COL1 DATE">06 Aug <span>21</span></td><td class="COL2 DOSES">4,289,913</td><td class="COL3 VAR"><span class="green-up"> </span></td><td class="COL4 NET"><span class="green">93,626</span></td></tr> local netChangeStart = line.find("NET\">") + 5; // Find where the net change start column is line = line.slice(netChangeStart); // Get rid of all data before that local firstCloseTag = line.find("</"); // Find the first close tag now (could be </span> or </td>) line = line.slice(0, firstCloseTag); // Remove everything including and after it local openTagEnd = line.find(">"); // Check to see if we have an open tag at the start if (openTagEnd != null) { line = line.slice(openTagEnd + 1); // if we do it is likely a span tag and needs removing } local jabs = -1; if (line != "-") { // Remove all commas and turn it into a number jabs = split(line, ",").reduce(function(previousValue, currentValue){ return (previousValue + currentValue); }).tointeger(); } server.log("Got " + jabs + " from CovidLive"); return jabs; } } return -1; }

device.on("requestUpdate", updateDevice);

Closing

Now that I have it updated and have tested that it is working (and fixed the edge cases I didn't think of when I was first coding it) I have it sitting somewhere a bit more visible on my desk, waiting for the day it first goes multicoloured.

Personally, I'm hoping it does that on tomorrow because I am getting jabbed today (and that's when today's numbers will be shown).


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK