Back in 2013, we had a problem in Denver: numerous employees… one restroom. To solve the constant problem of walking down the back hallway only to realize the restroom was occupied, I wrote a small .NET app so you could check the restroom status without leaving your desk.
Ah, 2013 – Windows 7 with Skype and a toilet in the tray
The old tool was retired around 2015 when we expanded the office and got a second restroom, and I really didn’t feel like rewriting (or rewiring) the program at the time.
It’s nearly 2020, and we just finished yet another office expansion. If you judge a business’ success by its number of restrooms, like I do, then Gravity is doing great – we are up to four.
But now, they are spread around the office, meaning you can spend some time hunting for an open room. There is only one solution…
As a disclaimer, I do projects like this to learn and stay on top of technology. It’s one thing to say “you should go serverless” and another to actually implement a serverless tool in production… tech theory often flies out the window when confronted with live users, continuous uptime and disparate systems (i.e. reality). There are a thousand ways to do this better, cheaper and more efficiently, but I’ve already spent far too many nights and weekends on a machine that watches toilets. Let’s keep it in perspective.
Each restroom uses a Samsung SmartThings contact sensor on the door to detect occupied/unoccupied. I originally considered using motion sensors to avoid the “somebody shut the door when they left” problem, but motion sensing would cause delay since you’d need to wait at least 20 seconds before declaring the room unoccupied -- we have all had the overly ambitious motion sensors that turn out the lights while you’re clearly still there.
I chose SmartThings because they seemed simple to hook to IFTTT to send an HTTP POST. Lots of smart home systems can do that, but this was a simple option.
The (Non) Server
I used AWS Lambda for the server-side code, and AWS API Gateway proxies requests from the internet. There are two endpoints:
First, a traditional REST API handles requests from the SmartThings door sensors. On state change, each sensor (via IFTTT) posts the sensor/restroom ID and whether it is now open or closed. This data is recorded in a DynamoDB instance. We have one set of data for the current status of everything, and another set of history data to track all restroom… uhh… “sessions” – more on that in a minute.
Next, I wanted to experiment with WebSockets. WebSocket is a protocol for persistent, full duplex communication over HTTP. My 2013 version of this app relied on clients polling the server every 15 seconds or so. Now, instead of polling, the app simply opens a connection with the API Gateway and keeps the connection open. When the server receives a door state change, it pushes it immediately to all connected clients.
AWS has a great RTC tutorial that I based my code on.
All I had to do is make one Lambda function (the logic behind the door API) call the other function (the logic behind the WebSocket API) so we know when to update clients. I thought this part would be hard and I’d have to have the Lambda function try to connect to the API of the other function… turns out Lambda.invoke makes calling one function from another super easy.
Full code available on GitHub: https://github.com/jpr-c8/rrmonitor
For the client app itself, I wanted to try Electron. This is a Node-based, write-once-run-everywhere framework. Again, this is where tech theory and user reality butt heads. I wanted to see how much work it took to compile an inherently non-native program for Windows and MacOS.
Step 1: I needed a simple UI to show which restrooms are open. The office blueprint was about as literal and functional as I could get. (Which is important. Function will always triumph, because form is dumb*.)
Alas, my design skills are horrific. Katie Phillips laid it out for me.
Availability (icon) by Seanau from the Noun Project. Unavailable (icon) by Andrejs Kirma from the Noun Project. https://thenounproject.com
Blueprints created by Stacey Twigg at Shopworks Architecture http://www.shopworksarc.com/
* Virtually no one I know agrees with this statement. I take an extreme viewpoint to help balance the form-at-all-costs state of the development world. And in case you don’t know the reference…
Step 2: Use the excellent ws library to hook the Node client to the AWS API Gateway.
Step 3: Put an icon in the system tray so you don’t have to open the app to get info. The number corresponds to the first available restroom.
Toilet icon made by Freepik https://www.freepik.com/home from www.flaticon.com
Step 4: Allow users to choose which restrooms are closest to their desk so they get better info. Calling it your “favorite” restroom sounded odd, even for this project, so I went with “preferred.”
Surprisingly, the only cross-platform oddities I ran into were around icons: sizing, OS menu icons, installer icons, etc. Actually making the program work on Mac and Windows was trivial. Making installers for each system was one of the few pieces that lacked cohesive documentation.
So, in short:
Does it use too much memory for a simple app? Yes.
Did it save a gajillion hours (plus future maintenance hours) to not write two copies of the tool? Also yes.
It’s a worthwhile tradeoff for this project.
My next step is to learn AWS SageMaker. Why? I am now collecting tons of historical restroom session data. In theory, machine learning can build a model that predicts the open/close times of each restroom door.
For example, is there a big rush right before the 11 am all-hands meeting? (Hint: There is.) I want to see if a machine can figure it out and, perhaps, alert interested parties to get ahead of the crowd.