Help with API Post to "Feed the Fish"

I am attempting the create a process allowing Hubitat to make an API post to my aquarium controller. Can someone provide guidance on how to create an app that will do this. Below is the post that needs to be made.

curl -X POST -c cookie.txt -d '{"user":"USERNAME", "password":"PASSWORD"}' http://IP_ADDRESS/auth/signin
curl -b cookie.txt http://IP_ADDRESS/api/macros/{id}/run

Something along these lines might work:

def Params = [ uri: "http://${ IP_ADDRESS}/api/macros/${ ID }/run", headers: [ Cookie: "{\"user\":\"${ USERNAME }\", \"password\":\"${ PASSWORD }\"}" ] ]
asynchttpGet( "ReceiveResponse", Params )

You need to have a function to receive the response that the aquarium controller returns which I labeled as ReceiveResponse.

On a different note... which aquarium controller are you using? I reached out to most of them over the last couple years to see about building drivers for them after I had already built my Neptune Systems Apex driver. Only a couple responded and they both said they had no interface (xml, json, api, or anything) that could be used to read data.

Thanks for the follow-up. Where exactly would I enter these lines? This is for a Reef-Pi controller that is set up on one of our smaller tanks. We use GHL on our large display tank.

You would enter them in whatever function you are going to use to trigger them. I mostly work on drivers but this type of thing is the same between the apps and drivers.

You can always take a look at the Example Apps Hubitat has provided for some basics.

For the aquarium controllers... I understand. I did not check on any of the opensource/DIY controllers (they can be highly variable after all) but I did reach out to GHL and they were one of the two that responded.

I am not a developer so I think I'm a bit over my head here.

Well, it does not need to be an app. You can send an HTTP Post from a Rule. Maybe it could be worked there rather than write a whole app? Sounds like a driver might be better anyways because that way you could add the aquarium controller in as a device right off the bat and have access to whatever information it makes available.

A great example of an authenticated http post from an app can be found here (Excellent app by @dman2306):

1 Like

Agreed. Unfortunately, this is far from my area of expertise so I would not even know where to start but I'll take a look. I appreciate you taking a minute to give your thoughts.

You can totally figure this out, especially since @dman2306's excellent example that @Brandon linked to is most of the way there.

Have you installed and used custom apps (or drivers) code on Hubitat before?

If you want to try it out and learn a bit, I'd do these steps:

  • Install this code as a custom app: https://raw.githubusercontent.com/dcmeglio/hubitat-rebooter/master/apps/Rebooter.groovy Then go back into the code editor and focus on the executeReboot() method.
  • Edit the first httpPost section to remove the whole query section and the submit entry in the body section. Modify the username entry name to just be user. Also update the uri to be the one you indicated in your first curl request line. After that, you should have an equivalent first request from Hubitat.
  • Edit the second httpPost section to remove the path line and update the uri to match your second curl request. Also, change it to httpGet method instead of httpPost. After that, you should have an equivalent second request from Hubitat.
  • Go and add the User App and configure it to use a button for rebooting. Any button device should do, either physical or virtual. Also enter your password and username.

If the app saves and installs correctly, try pressing that button. If it works, you're on your way and you can do some renaming, stripping out extraneous code, and adding other cool features.

@tomw thanks for the little step by step. I decided to give it a shot and figured with the help of the community I should get this to work.

I followed the steps you provided and have the app installed along with a virtual button. When I press the virtual button set up to execute the app, it did not work and I receive the following error in the logs:

app:3942020-12-13 09:22:58.325 errorgroovyx.net.http.HttpResponseException: Bad Request on line 98 (buttonPushed)
app:3942020-12-13 09:22:58.266 infoRestarting hub
dev:2612020-12-13 09:22:58.194 infoButton (Virtual) - Feed the fish button 1 was pushed

When I look at line 98, this is the beginning of the first "httpPost" section. This is what I have entered for this section:

httpPost(
[
uri: "http://{IP_ADDESS_TO_CONTROLLER}",
path: "/auth/signin",
body:
[
user: rebootUsername,
password: rebootPassword,
]
]
)
{ resp ->
cookie = resp?.headers?.'Set-Cookie'?.split(';')?.getAt(0)
}

Why thoughts on how I can get past this error?

The server doesn't like your request for some reason. Bad Request corresponds to HTTP error 400. My guess is that content types are getting mixed up because of the data type in Groovy.

Try it like this:

httpPost([uri: "http://{IP_ADDESS_TO_CONTROLLER}/auth/signin", body: "{\"user\": \"${username}\", \"password\": \"${password}\"}"])
{ resp ->  cookie = resp?.headers?.'Set-Cookie'?.split(';')?.getAt(0) }

You could probably do it like this, too:

void httpPostJson([uri: "http://{IP_ADDESS_TO_CONTROLLER}", path: "auth/signin"], [
user: rebootUsername, password: rebootPassword])
{ resp ->  cookie = resp?.headers?.'Set-Cookie'?.split(';')?.getAt(0) }

Thanks for the follow-up. Using this seems to work. I really appreciate the help. I did make one change to your suggestions though.

I used:

"{\"user\": \"${rebootUsername}\", \"password\": \"${rebootPassword}\"}"

Instead of:

"{\"user\": \"${username}\", \"password\": \"${password}\"}"

So it would pull the user and password set up in the rebootSecurityarea. Also, when I used "httpGet" in the second HTTP section I received an "Method Not Allowed on line 98" error. I changed it to "httpPost" instead and that seemed to work.

Now one other issue I discovered in the logs. Shortly after I run this the following shows in the logs:

groovy.lang.MissingMethodException: No signature of method: user_app_dcm_rebooter_Rebooter_225.logDebug() is applicable for argument types: (java.lang.String) values: [uninstalling app] on line 61 (uninstalled)

When I look at this line in the app, it appears this is for some logging for uninstalling the app:

def uninstalled() {
logDebug "uninstalling app"
unschedule()
unsubscribe()}

Any idea on what is causing this?

I'm not sure how that logDebug crept in, since I don't see it elsewhere. It's basically trying to execute a method that doesn't exist. It should help if you change that line to:

log.debug "uninstalling app"

I don't understand the "Method Not Allowed" error. Could you post the rest of the log as well as what is on line 98 after your modifications?

When you say you switched it to httpPost, does that mean that the fish tank does what you want it to now?

I tried to change it to log-debug and it would not save. It gave this error:

unexpected token: uninstalling app

I'm not to worried about this. I work on this another time.

It looks like the "Method Not Allowed" was a response sent from the aquarium controlled. I'm guessing it is because the API action I was trying to perform was a Post action and not a Get action. After I changed to the httpPost it worked fine and no longer gave me the error. The fish tank is now doing that I wanted it to do. Thanks again!!

1 Like

Awesome!

Just quick note- it should be log.debug, not log-debug. Or you can just remove that line. :slight_smile:

I'm glad it's working now. What features are you going to add next?

Sorry, it was log.debug I tried. Not log-debug. I will likely just remove the line.

I'm going to add the option to include the MacroID as a variable in the url so I do not need to create a new app for each Macro that I want to run on the aquarium controller.

The aquarium controller has an extensive API that allows you to do allot. For the most part, anything you can see and do from the web interface, you can do from the API. At some point I would like to be able to pull information from the controller as well to display in the Hubitat dashboard. Like the temp, ph, etc. Set up push alerts sent to the Hubitat mobile app. Possibly even control other aspects of the controller like dosing pumps, lighting schedule, etc.

I know of many that would like to use hubitat as an aquarium controller. The issue is most of the sensors and controls that are needed simply are not available in a zwave or zigbee format or are priced at a rate that does not make it cost-effective once you start adding them up. Conductivity sensors, ph sensors, water level sensors, flow sensors, 0-10v control, etc. Due to the API of Reef-Pi, the controller I am using, I will be able to overcome many of these limitations. This is a link to the API documentation if anyone else comes across this post who is interested. Reef-Pi API

Now if only I was a programmer, I would have the knowledge to do all of this. I'll pick away at this and hopefully learn a bit as I go.

1 Like

On a side note, do happen to know how I can make the name editable when I create the user app? Or where I can go to learn how to do this? When creating a new instance of the user app, it just uses the app "name" hardcoded in the "user app code"? If I create several apps they will all have the same name so it will be hard to distinguish one from the other?

I use a pattern that I learned from @jwetzel1492's apps.

For example:

You need a parent app whose purpose is to configure and install multiple instance of its own child apps. This is how things like Rule Machine are structured, too.

Your app that actually does the instance-specific things (like talking to different tanks or whatever) would be the child. The parent is a shell that knows how to configure itself and gives the necessary menus for a user to install children. You could use this (and the other child apps in the folder) as an example: Hubitat-Combined-Presence/combinedPresence.groovy at master · joelwetzel/Hubitat-Combined-Presence · GitHub

Perfect. Thanks for the info.

1 Like