It's quite cool honestly, well done to the developer. It works, so it's de facto a good solution.

> The hard parts weren’t the architectural pattern - they were embedded systems constraints. Tuning buffer sizes and designing components shallow enough to fit in 512 bytes required iteration.

I am a bit confused by the choice to use HATEOAS for this. Given the hardware constraints of the controllers I wouldn't have figured that doing handlebars style html templating is a good use of resources, rather than just sending raw data to the client and having it handle the presentation. I probably would have returned a blob of data from the servers and handled everything on the client, but I also am a UI guy first and foremost...

Hi! I'm the author. Great question.

"I probably would have returned a blob of data from the servers" That would certainly work, but adding new functionality to the rooms would require changes on the controllers (implementing the new JSON API) and on the client (calling, handling, and rendering the new feature). I wanted to minimize development time for myself now and make it possible to add new features using only C++ later.

Having the server declare the supported actions (not all rooms support the same features) and how to invoke them accomplished that. This independent evolution was a big reason why I leaned into HATEOAS.

"wouldn't have figured that doing handlebars style html templating is a good use of resources" I didn't do a size comparison between the HTML fragment and a JSON response. Next time I'm at the house I'll come up with some measurements and add it to the article - I think it's interesting. That said, the performance was more than enough for the business and each of the controllers still had plenty or RAM and Flash Memory available, I didn't need to optimize this area. If I was going to, I'd want to avoid going through the buffer so many times.

That said, the simplicity and robustness of the HATEOAS approach were the main goals. I wouldn't use it for something like high-frequency trading where every byte matters.

That's the assumption and motivation:

> I wanted to create an architecture that minimized future work on both the admin app and the controller network layer. That way, the business could lean on its in-house development experience to make changes after the project.

From your perspective as a UI developer, you would need to update all clients (in the building) if something on the server changes, e.g. if a new button or game state is added. HATEOAS is a good architectural choice if you want to minimise the likelihood of having to change the client implementation.

I use HATEOAS as much as possible in my daily work. It is a good way to think about state, state transitions and what capabilities a client must have. That's the actual part of the author's motivation. It provides the "in-house development" with paradigms how to make changes.

It sounds like there is one client and multiple servers, so I assumed that it would be easier to change the single client versus multiple servers. I guess it also depends on what the in-house developers are experienced with.

But I'm not so sure that it really minimises future work. Like if they decided they want a button that toggles both the sound and lights in a room, that's trivially implemented on the client. Or if they want to reposition a button from the top of a stack to the bottom. My assumption rests on the idea that changing the client is easier though.

"it would be easier to change the single client versus multiple servers" + "trivially implemented on the client" It would be! That said, adding a new feature requires implementing that feature on the server, so requiring an additional change on the client is actually more work.

For example, one of the rooms in the house just installed house lighting to help with resetting rooms between guest groups. One one of the controllers has this.

Once I implemented the hardware logic, I updated the server's state response to include a new button that pointed to a new house lights endpoint. That button immediately showed up in the admin app. Super fun DevEx, and this is what I meant by minimize future work - minimal work to expose new room functionality.

"My assumption rests on the idea that changing the client is easier though" Think about the number of changes required to implement the feature end-to-end. You're right that updating N controller C++ templates for a button is wild, but that's not the entirety of the work. Those controllers already needed to be updated to handle what that button does. Adding the button itself is minimal work (copy-paste, really) in comparison to the feature work.

I agree.

If I understand correctly the hard parts are the limited resources of the controllers and a need for high versatility for the rooms. Actually needing to update the controllers to update CSS for the main app is kind of crazy.

With my limited understanding of the system it feels like my go to solution would be: - A thin generic program on the controller that run a arbitrary configuration of room logic. - A main client program that can: 1. manage the state of the current configuration. 2. push / switch configurations on the controllers.

Hi! I'm the author.

"Actually needing to update the controllers to update CSS for the main app is kind of crazy." The controllers don't return CSS: they return HTML fragments. I chose not to include CSS in the response because of the reasons you're thinking, plus the servers shouldn't really care about how their state looks.

The admin app styles the custom tags. I also figured this would make it easier for them to contract out changes to that app.

"the hard parts are the limited resources of the controllers and a need for high versatility for the rooms." Yep! I didn't cover it in the article because it didn't fit the theme, but another challenge was the single execution thread. When you have a bunch of rooms on the same controller, you want to avoid blocking that loop, which is what the built-in Arduino delay function does. I came up with a timer system for non-blocking time-based triggers.

That is pretty much what I do, although my "thin generic program" is still very beta.

I follow the narrow waist principle, and basically have all communication in the form of setting and subscribing to "tag points", plus getting their metadata.

The controllers have some ability to run logic, in the form of web editable "If you are in this state, every frame, set this variable to this expression", and they have a generic UI to set and view all the variables.

But I don't have any legacy controllers to deal with, just ESP32s.

If I do run into legacy stuff, I just leave it alone, my main controller lets you write plugins that make whatever device you want appear as one of these tag point based devices, so I keep as much of the flexibility as possible in nice version controllable python.