Skip to main content

· 6 min read
John Ottenlips Franke

Hey, I'm John Ottenlips Franke, a member of the platform team at Vestaboard. I'm excited to share my journey of preparing for the fall rock climbing season and how Vestaboard, along with GitHub Actions, played a crucial role in my successful off-wall training routine.

The Crux of Consistency

Last fall, I had some big rock climbing trips planned, including following my first multi-pitch on southern Utah sandstone near Zion National Park and leading classic Southeastern US routes like "Tarantella (5.10a)", "Christine (5.10a)", and "Pet Sematary (5.11a)" at the Wild and Scenic Obed. My goal was to climb the routes as cleanly as possible with minimal falls or breaks. I knew I needed to up my game with a consistent routine, having struggled on a few longer outdoor climbs earlier in Winter and Spring.

My previous attempts at training were all over the place - random and mostly social gym sessions weren't cutting it. I realized I needed a more focused and structured approach to give the upcoming season my best effort. However, planning a routine and sticking to it can be very challenging.

Vestaboard: A Visual Reminder for Success

Enter Vestaboard. With Vestaboard, I could experience an inspiring daily reminder for my full-body calistenic workout routine, also known as a WOD or workout of the day. And the best part? I could automate the schedule of these reminders and the exercise choice with a small TypeScript file and GitHub Actions, ensuring I stayed consistent and had a variety of climbing movements to practice. This project wasn't my only form of training, but it was helpful in keeping me on track off the wall.

Planning the WOD

A key factor was planning small sets and reps that were not too aggressive. A steady and measured approach has proved more effective than my prior sporadic and intense sessions. It is important to note that I am not a personal trainer, just a hobbyist climber with internet access, so please consult a trainer if you plan on making your own workout routine. I got most of my exercises from YouTube and climbing blogs. I experimented with many different movements of varying intensities and found a set of exercises I could stick with and build on.

Tying together Vestaboard and GitHub

If you follow our Subscription API documentation, you can make your subscription in our web application and get a subscription key, secret, and ID to display custom messages on your Vestaboard. GitHub Actions can store these secrets securely. In programming and climbing, it is import to practice safety to a high degree. Let's keep those secrets safe!

Climb On: GitHub Actions in Action

GitHub Actions and Vestaboard turned my climbing aspirations into a well-defined routine, giving me an easy to read and timely display for each workout and making the entire process efficient and effective. To create a GitHub Action that runs on a schedule, you need a YAML file in the workflows folder with your cron expression and the bash scripts you want to run. In the root of your project, you can add a file and folder structure like .github/workflows/WODaboard.yaml.

name: "WODaboard"
on:
schedule:
# UTC zone
# daily @ 8 am CST
- cron: "0 13 * * *"
# or run on dispatch
workflow_dispatch:

jobs:
wod:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- run: |
bun install
bun run index.ts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VB_SUB_ID: ${{ secrets.VB_SUB_ID }}
VB_SUB_KEY: ${{ secrets.VB_SUB_KEY }}
VB_SUB_SECRET: ${{ secrets.VB_SUB_SECRET }}

Since I had some slightly more complicated logic than just posting a static message to my Vestaboard, I used a Typescript function I could run with bun. Bun lets us run TypeScript files without having to compile them to JavaScript first.

Here is a simplified version of the code that sends plain text to my Vestaboard.

// index.ts
// full code available on GitHub https://github.com/jottenlips/wodaboard
const sendMessage = async (text: string) => {
if (process.env.VB_SUB_KEY && process.env.VB_SUB_SECRET) {
await fetch(`${API_URL}/subscriptions/${process.env.VB_SUB_ID}/message`, {
method: "POST",
headers: {
"X-Vestaboard-Api-Key": process.env.VB_SUB_KEY,
"X-Vestaboard-Api-Secret": process.env.VB_SUB_SECRET,
"Content-Type: application/json"
},
body: JSON.stringify({
text,
}),
});
}
};

sendMessage("Hello World!")

This function can let us post the content we want to Vestaboard via the Subscription we created earlier.

  • Be sure to use process.env so you don't accidently commit your secrets to source code.

Reaching Beyond Scheduled GitHub Action

Notice the additional field workflow_dispatch under on in the Action example above. This configuration lets us run the Action from GitHub whenever we want via the Actions tab on your repository. workflow_dispatch can be very helpful when developing an Action to run on command, like displaying the workout immediately.

GitHub Actions isn't just about workout reminders for me. I also found another valuable use – deployment notifications. Setting up Vestaboard to alert me when a new build deploys enhances my awareness of the development process. Integrating with Vestaboard is valuable on any GitHub project where you have automated deployments and want to know when they have succeeded without checking the Action status. To accomplish this, we use the push field so that when a change happens to the main branch, the Action will run.

For this GitHub Action, I used curl instead of the TypeScript code since I have very static content to publish to Vestaboard.

// .github/workflows/notify-deployment.yaml
name: "notify-deployment"
on:
push:
branches:
- main

jobs:
wod:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest
- run: |
curl -X POST -H "x-vestaboard-api-key: $VB_SUB_KEY" -H "x-vestaboard-api-secret: $VB_SUB_SECRET" -H "Content-Type: application/json" -d '{"text": "{64} New Wodaboard Deployment! {64}"}' https://subscriptions.vestaboard.com/subscriptions/$VB_SUB_ID/message
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VB_SUB_ID: ${{ secrets.VB_SUB_ID }}
VB_SUB_KEY: ${{ secrets.VB_SUB_KEY }}
VB_SUB_SECRET: ${{ secrets.VB_SUB_SECRET }}

Conclusion

Thanks to Vestaboard and GitHub Actions, I met my climbing goals. I was able to lead-climb all three of the Obed routes with minimal falls or breaks. I even flashed one of the climbs with no falls or resting on the rope. I also made it up the Utah multi-pitch smoothly. Repelling down the 5 vertical pitches was a lot scarier than going back up 😅. The combination of visual reminders, automation, and structured workouts propelled me to climb a full grade harder than the previous year. I even completed a few v6 boulders in the gym this year.

I hope you consider integrating the Vestaboard API into your upcoming projects, whether for staying on track with your goals or creating something delightful and inspiring.

Code Example

Wodaboard Source Code

WOD

🧗‍♂️

Utah

Obed

· 3 min read
Tyson Cadenhead

Milo is my oldest son. He is 12 years old and he is non-verbal. Since his autism diagnosis 10 years ago, we have tried just about every augmentative and alternative communication (AAC) iPad app available to help him to communicate with us.

A couple of months ago, I was setting up some buttons on TouchChat when I saw that there was an option to add a WebHook as a button action. Right away, my wheels started turning. I have a Vestaboard mounted on the wall in my upstairs home office. What if Milo could send me messages during the day from his "talker"?

I started by adding a few buttons with messages that Milo could send to our Vestaboard.

Once I started digging into the WebHook implementation in the TouchChat app, I realized that they did not support custom headers for the webhooks, so I knew I was going to need to spin up a simple service to send messages to my board with our Read/Write API

I created an extremely simple Ampt application on my personal account to make the requests to the Read/Write API. TouchChat could accept a JSON payload for the POST body. I set up my service to expect a hard-coded token using the Ampt params package and a text string with the message to be sent.

Here is the entire codebase for the Vestaboard TouchChat application:

import { http } from "@ampt/sdk";
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import fetch from "node-fetch";

const app = express();

app.use(cors());
app.use(bodyParser.json());

app.post("/", async (req, res) => {
if (req.body.token !== process.env.TOKEN) {
return res.status(401).send({
message: "Unauthorized",
});
}

await fetch("https://rw.vestaboard.com/", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Vestaboard-Read-Write-Key": process.env.VESTABOARD_KEY,
},
body: JSON.stringify({
text: req.body.text,
}),
}).then((res) => res.json());

res.send({
success: true,
});
});

http.node.use(app);

Finally, I updated all of the button actions in the "Vestaboard" screen I created on TouchChat. I setup a WebHook with a custom message for each button.

In a short amount of time, I had a completely working prototype that allowed Milo to send me messages anytime. The idea is that we could add more messages over time as his vocabulary and needs expand. Adding new messages would not require an update to the Ampt service, because the message itself is coming from TouchChat.

Here is one of the messages that Milo can send to the Vestaboard:

I realize that this is a one-off use-case that may be a bit esoteric for most Vestaboard developers, but this basic concept of using the Read/Write API with other applications' WebHooks can apply pretty universally. If you want to dig in, the source code is hosted on GitHub

This example may not change the world, but it might change our family. Vestaboard is empowering Milo to communicate in a new way!

· 4 min read
Tyson Cadenhead

Ampt is a Serverless framework that inverts the infrastructure as code paradigm to an even more intuitive idea of infrastructure in code. This allows you to quickly create scalable applications without the overhead of provisioning resources.

As of today, Ampt is officially launching to the public. We have been using Ampt at Vestaboard for several months now to simplify our infrastructure and as the backbone for many of our new Vestaboard+ features. The launch of Ampt is an excellent opportunity to showcase how simple it is to schedule and automate Vestaboard messages on a cadence.

Get Started with Ampt

The first thing to do is sign up for your own Ampt account. From there, you just need to install the Ampt CLI by running:

npm i -g @ampt/cli
ampt

Initially, this will prompt you to log in to your Ampt account. After you have, you can select "Create new app" from the CLI and choose a starter template. For our purpose, we'll go with "TypeScript API (Express)." We can name the app whatever we want, so how about something like "vestaboard-custom-clock" and we're all set. Ampt will spin up a new application and even start up a developer sandbox for you that is a fully operational serverless project.

In the root of your newly created project, there will be an index.ts file. Go ahead and delete all the code in there and import the task from Ampt. We'll use that to schedule a task. To make sure it's working as expected, you can just drop in a console.log. Let's make a task that logs every 1 minute:

import { task } from "@ampt/sdk";

task("vestaboard-clock", async (event) => {
const timestamp = `${new Date().toDateString()} ${new Date().toLocaleTimeString()}`;
console.log(`It is ${timestamp}`);
}).every("1 minute");

This will log every 1 minute with something like:

It is Mon Sep 18 2023 1:34:16 PM

It is important to note that you can also use tasks with a cron expression or to run a task once at a specific time. If you want to explore more options, check out the Ampt documentation.

Send a Message to your Vestaboard

Now, we just need to send a message to your Vestaboard when the scheduled task is fired. The first step is to enable the Vestaboard Read/Write API in order to get your token.

Ampt has a dashboard at https://ampt.dev/ where you can query data, set environment variables, see production logs and more. Since the Read/Write API token is a secret, you don't want to store that in your code, but instead, you'll want to add it as an environment variable in the "parameters" tab of the Ampt dashboard. Let's call it VESTABOARD_TOKEN. You can access it in code using the params export:

import { task, params } from "@ampt/sdk";

task("vestaboard-clock", async (event) => {
const VESTABOARD_TOKEN = params("VESTABOARD_TOKEN");
const timestamp = `${new Date().toDateString()} ${new Date().toLocaleTimeString()}`;
console.log(`It is ${timestamp}`);
}).every("1 minute");

Now, we just need to install a fetch package:

npm i fetch@2 --save

and make a request to the Read/Write API with our token:

import { task, params } from "@ampt/sdk";

task("vestaboard-clock", async (event) => {
const VESTABOARD_TOKEN = params("VESTABOARD_TOKEN");
const timestamp = `${new Date().toDateString()} ${new Date().toLocaleTimeString()}`;

await fetch("https://rw.vestaboard.com/", {
body: JSON.stringify({ text: `It is ${timestamp}` }),
headers: {
"Content-Type": "application/json",
"X-Vestaboard-Read-Write-Key": VESTABOARD_TOKEN,
},
method: "POST",
});
}).every("1 minute");

That's it. With just a few lines of code, you've set up a scheduled task in Ampt that automates your Vestaboard. Imagine the possibilities!

If you'd like, you can clone our repo on GitHub with a full working example.

· 2 min read
Tyson Cadenhead

I came on full-time at Vestaboard about a year ago and started the process of discovering what we already had as well as taking part in building a vision for what the future of Vestaboard engineering will look like. Over the past year, we've built up our internal engineering team and focused heavily on new feature development as well as handling ongoing bug fixes and tech debt.

Our mantra has been to simplify the Vestaboard platform and to make it more scalable and resilient.

The truth is, our flagship product is very simple: we have a beautiful board that displays messages when you send them.

In the process of revamping our documentation, and in rebooting the Vestaboard Developer Community, we hope to bring back the fun and simplicity that makes Vestaboard so appealing as a software engineer in the first place.

You may notice that the new Subscriptions API is simpler than the old version. This is in part to stop exposing internal data structures that get in the way of quickly working with our APIs. It is also to dial things back and start to understand what endpoints provide value to you, the developer, so we can harden them and make them work better for everyone going forward.

In the coming weeks, we will be adding more content here. We will be creating quick-start guides to simplify the process of sending messages. We will provide solutions we've used and solutions we've seen in the wild that make building integrations and tasks that send messages easier.

If you haven't already, please join our Slack community to stay up to date on everything and we would absolutely love any feedback on how we can make the developer experience better.