tagyoureit / nodejs-poolController

An application to control pool equipment from various manufacturers.
GNU Affero General Public License v3.0
323 stars 94 forks source link

Is the API intended to be REST, or RPC over HTTP? #309

Closed colin-young closed 3 years ago

colin-young commented 3 years ago

What is the overarching design guidance for the API? I'm guessing that much of the structure/organization has been informed by the makeup of Pentair's Intelli-whatever panel UI, but I'm also guessing they don't publish an API for them.

Also, is the documentation at https://tagyoureit.github.io/nodejs-poolcontroller-api/ kept in sync with the main code? And why is it not included in the main code repository?

rstrouse commented 3 years ago

There is a whole lot going on under the hood to get all the interfaces working. For the most part the state and config APIs are designed to be REST. However, we quickly found out that many of the automation targets out there do not do well with a pure state representation and RPC is just too granular for the types of equipment we are dealing with. So you will see variants to streamline the integration. For instance, there is PUT: /state/chlorinator {"id": xxx, "poolSetpoint?": number, "spaSetpoint?": number, "superChlorHours?": number, "superChlorinate": true | false } PUT: /state/setChlor {"id": xxx, "poolSetpoint?": number, "spaSetpoint?": number, "superChlorHours?": number, "superChlorinate": true | false } PUT: /state/chlorinator/poolSetpoint {"id": xxx, "poolSetpoint": number } PUT: /state/chlorinator/spaSetpoint {"id": xxx, "spaSetpoint": number } PUT: /state/chlorinator/superChlorHours {"id": xxxx, "superChlorHours": number } PUT: /state/chlorinator/superChlorinate { "id": xxxx, "superChlorHours?": number, "superChlorinate": true | false }

All of these routes can be used to manipulate the state of the chlorinator for any of the supported OCP/Nixie platforms. Bear in mind there are two separate but affiliated data management areas for the equipment. These include the state (volatile) and config (persistent) data areas of any piece of pool equipment. The lines between these two blur because the true state of any equipment item is a combination of the two.

So that being said look at the API as a REST api. To make changes on the server use the PUT verb and to get the current state use the GET verb. We rarely use POST because that contradicts the data structures found on the controllers when we are operating as a slave controller. And only use SEARCH verb to actively search for equipment or servers.

To set information use the PUT verb. Most of the data elements on the payload are optional. The exception being the elements that are used to identify the particular piece of equipment. For instance, if you only want to change a particular set of attributes on the equipment, omit the items you don't want to change, Now we did have to make some adjustments to this because there are a bunch of endpoints out there that expect an RPC style call so we integrated some of those into the mix as well. For instance, you call PUT: state/circuit/setState {"id": xxx, "state": true | false} to set a target state on a circuit, feature, or group.

To get information about the equipment use the GET verb. This will return the true state representation including the configuration items for the unit of equipment.

Now bear in mind that this software works with a variety of controller platforms. There are 12 EasyTouch OCPs, 8 IntelliTouch, and 6 IntelliCenter. Then there are the Nixie controllers with an endless number of configuration options. All of these use completely different communication protocols under the hood and their state information will change depending on the capabilities.

Now there is a full blown socket interface as well as a myriad of command level interface functions that you can customize yourself using your own bindings. These can communicate using MQTT, or Http(s).

There is no SOAP interface or XML-RPC integrated into application. We originally had plans to support it but the weight was just too heavy.

colin-young commented 3 years ago

Thanks. That explains a lot and confirms my suspicions, and puts things into context for when I run into any bugs or have a feature request. Am I correct in assuming the Swagger docs are not "connected" to the source, i.e. they are manually created?

Regarding PUT vs POST: (full disclosure: I'm a bit of a REST purist) you'd only want to use POST when you are creating a new resource (e.g. adding a new pump into the system), but you'd also need the controller platforms to support that pattern. I'm really just using this project to talk to my pump (and, eventually, chlorinator once I get a 10V power supply installed), so I'm really only interested in the pump and chlorinator status and control. The existing endpoints support that pattern, even if they are, IMHO, a bit verbose (see my point about being a REST purist above).

Now there is a full blown socket interface as well as a myriad of command level interface functions that you can customize yourself using your own bindings. These can communicate using MQTT, or Http(s).

Is there documentation somewhere? I found the MQTT integration project, but that seems to be for an older version of this project only.

Thank you for not perpetuating SOAP or XML-RPC :)

Fun story: for hiring I like to use a design exercise during the interview that asks candidates to create a REST API for managing an industrial process involving pumps, valves and tanks. I got some interesting reactions, mostly along the lines of "but it's not HTML, you can't do that." A bit of prompting usually gets them heading in the right direction, but I've found it's a really good way to find out how well they understand REST and API design. It's also a good way to find out which candidates used to be mechanical engineers.

tagyoureit commented 3 years ago

We have the MQTT interface documented in the wiki.

And the API docs are not automatically created from the code. I've tried a few solutions but they would need to go a few layers down and understand the logic so, unfortunately, it is all manual at this point. And hence is usually a bit behind.

And why is it not included in the main code repository?

Mostly because it's part of an automated build process and was just easier to keep it separate.

colin-young commented 3 years ago

Thanks! Somehow I managed to miss the Bindings link. Text must be too large and too close to the top of the page list :)

The first answer made the second obvious. One of the many reasons I'm not a fan of Swagger. But the alternatives are a) chatty and b) not really a good fit for the API you need to support here.