Super Chemex Bot: IoT and love of coffee combined

At Contentful, we drink a lot of coffee. And while there are many ways to brew coffee, Chemex is one of our favorites. It’s fun, simple, and you can make enough for the whole team.

Back when everyone sat in one small office, we had an actual doorbell in the kitchen. Whenever someone made a fresh batch of Chemex, they’d ring the bell, and regardless of where you were in the office, you could hear it. But over the past two years, the company has grown, and that solution didn’t scale.

Our Berlin office spans multiple floors and adjacent buildings, with each floor featuring its own kitchen and a dedicated Slack channel for communication. We wanted a button in every kitchen to post a notification to that area’s Slack channel when there's coffee available.

We got our hands on some AWS IoT buttons—the programmable version of Amazon Dash buttons—and decided to hack a solution. The basic idea was to have the button trigger an AWS Lambda function that would then emit a message to the right channel using the Slack API. We stored our configurations, such as mappings between buttons and Slack channels, along with the content of the messages in Contentful. This allows anyone to change these around without pushing code each time.

The first step was to register each Dash button with the AWS IoT platform. This process involved connecting a computer to the button’s wifi network and following the wizard to generate a certificate and private key. Then we copied those onto the button.

Next, we switched over to the Lambda dashboard in AWS and created a new function. We selected Node.js as the runtime so we could use the Slack and Contentful JavaScript SDKs. Under Triggers, we added each configured button as a trigger to this function. Now our Lambda function will receive the button press event as an argument. CloudWatch shows us that when the button is pressed, our function is called. Awesome!

After that, we set up the data in Contentful. We created two content types—Configuration and Message. Each configuration entry contains the button’s serial number and its respective Slack channel's name. Each message contains the text—and our bot will pick one at random to post on the Slack channel. The entries look like so:

We created our Slack application, gave it a name, description, and permissions. Then we copied the generated client ID and secret into Lambda environment variables.

The final step was to finish writing the Lambda function. It goes something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

exports.handler=(event)=>{// Fetch data from Contentful containing all configs and messagescontentfulClient.getEntries().then(prepareContentfulData).then(({configs,messages})=>{const{channel}=configs[event.serialNumber]// Select a message at random from the listconst{message}=messages[Math.floor(Math.random()*messages.length)]// Post it to slackslackWebClient.chat.postMessage(channel,message)})}

We pushed the code to Lambda with the AWS CLI tool, and glued the first button to the table in the kitchen of the engineering floor and went home. The next morning we announced it on Slack.

Allowing everyone to come up with new messages sparked some creativity.

Since we deployed this to the engineering floor in April, our team pressed that one button 275 times. The average Chemex is around 900mL. That’s almost 250 liters of coffee in 4.5 months on just one floor. Not bad!