Http POST

Managed to get this working using syntax...

username=user&apiKey=key&gatewayId=id&taplinkerId=id&action=true&duration=1

Just need to figure out how I can parse the response to deal with any errors...

From RM I don't believe that is possible. You would have to do that in the driver. If you look at the HTTP Get switch driver, you can see an example:

def on() {
    if (logEnable) log.debug "Sending on GET request to [${settings.onURI}]"

    try {
        httpGet(settings.onURI) { resp ->
            if (resp.success) {
                sendEvent(name: "switch", value: "on", isStateChange: true)
            }
            if (logEnable)
                if (resp.data) log.debug "${resp.data}"
			if (autooff)
				runIn(delay, off)
        }
    } catch (Exception e) {
        log.warn "Call to on failed: ${e.message}"
    }
}

the "catch exception" would be where the error occurred and the "resp.success" is it working. If the system is going to respond with some type of data, that would be in the resp.data field, which could then be parsed however you want.

1 Like

Sorry for a newbie question. I have a similar problem as described in this thread, but can't figure out how I can install this virtual device? I.e, where can I find the code? :slight_smile:

I want to try it with the AiLight REST-api

The above screenshot you posted is not from a device. It is actually from Rule Machine. That's why it is hard to find! :wink:

You can use a Virtual Switch device (simply add a virtual device and assign it the virtual switch driver, no new code required.) Then, in Rule Machine, create a Rule that is triggered by the Virtual Switch changing. Within the Actions of that rule, add a HTTP action for ON and and another for OFF.

Thank you! for the quick response. I'm getting closer :smile:
Question now is what kind of syntax I need in the 'Enter body for POST' box.

Tried some stuff but can't get it to work...

This is the curl command that works today:
curl -X POST http://192.168.1.2/api/light -H 'API-Key: hinotori' -H 'Content-Type: application/json' -d '{"brightness": 36, "state": "ON"}'

-H is header in curl (-H 'API-Key: hinotori'). -d is the data portion of the command and that is what you would put in the body for the post ('{"brightness": 36, "state": "ON"}'). However, AFAIK, you can't modify headers with RM so it probably wouldn't work anyhow.

The only way I can think of to get curl command to work is by either creating a custom app in Groovy (which is actually really simple to do) OR use something like NodeRed or NodeJS to craft and send the command using the event socket output from Hubitat.

Ok. Thank you for the support! I think a third option is MQTT. I will look in to it :smiley:

1 Like

If you figure out how to do it, can you please post how you did it, so that we can learn how to do that sort of stuff?

You could go that route (should the device you are trying to control supports it), but you'll still need to go the Groovy route to use it. AFAIK, the MQTT client that HE built in is in code only and there is no UI interface for it.

1 Like

I solved it with some dirty hard coded stuff (I'm not a programmer)
Check this pastebin out and search for HARD CODED

https://pastebin.com/4H1LCfsq

1 Like

I bet you're going to become one though... :wink: LOL

Nicely done, BTW (hard coded or not).

1 Like

I friend helped me a bit to change the code from a momentary switch to a normal one. He is not a Hubitat owner and we coded over facebook messenger, so don't expect quality code :smile: I just post it here as a reference if someone wants to build a "real" driver.

Headers and the data part that switch my bulb on and off are hard coded.
https://pastebin.com/hyWG9Nd0

/**

  • QUICK AND DIRTY FORK OF THE HTTP Momentary Switch to a normal switch
  • with hard coded headers. Maybe someone with skills can do a neat version
  • HTTP Momentary Switch
  • Copyright 2018 Daniel Ogorchock
  • Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  • in compliance with the License. You may obtain a copy of the License at:
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  • on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  • for the specific language governing permissions and limitations under the License.
  • Change History:
  • Date Who What

  • 2018-02-18 Dan Ogorchock Original Creation

*/
metadata {
definition (name: "HTTP Switch Hard coded", namespace: "ogiewon", author: "Dan Ogorchock") {
capability "Switch"
capability "Momentary"
}

preferences {
    input(name: "deviceIP", type: "string", title:"Device IP Address", description: "Enter IP Address of your HTTP server", required: true, displayDuringSetup: true)
    input(name: "devicePort", type: "string", title:"Device Port", description: "Enter Port of your HTTP server (defaults to 80)", defaultValue: "80", required: false, displayDuringSetup: true)
    input(name: "devicePath", type: "string", title:"URL Path", description: "Rest of the URL, include forward slash.", displayDuringSetup: true)
    input(name: "deviceMethod", type: "enum", title: "POST, GET, or PUT", options: ["POST","GET","PUT"], defaultValue: "POST", required: true, displayDuringSetup: true)
}

}

def parse(String description) {
log.debug(description)
}

def push() {
//toggle the switch to generate events for anything that is subscribed
sendEvent(name: "switch", value: "on", isStateChange: true)
runIn(1, toggleOff)
runCmd(devicePath, deviceMethod, "")
}

def toggleOff() {
sendEvent(name: "switch", value: "off", isStateChange: true)
}

// hard coded data for on and off command to my lightbulb

def on() {
sendEvent(name: "switch", value: "on", isStateChange: true)
runCmd(devicePath, deviceMethod, '{"brightness": 100, "state": "ON"}')
}

def off() {
sendEvent(name: "switch", value: "off", isStateChange: true)
runCmd(devicePath, deviceMethod, '{"brightness": 100, "state": "OFF"}')
}

//Added body as input

def runCmd(String varCommand, String method, String the_body) {
def localDevicePort = (devicePort==null) ? "80" : devicePort
def path = varCommand

def headers = [:]
headers.put("HOST", "${deviceIP}:${localDevicePort}")

//Hard coded headers. Adjust for your needs
headers.put("API-Key", "password")
headers.put("Content-Type", "application/json")


try {
    def hubAction = new hubitat.device.HubAction(
        method: method,
        path: path,
       
        //NYTT: Body från function call
        body: the_body,
        headers: headers
        )
    log.debug hubAction
    return hubAction
}
catch (Exception e) {
    log.debug "runCmd hit exception ${e} on ${hubAction}"
}  

}

Hi @duelago, @daniel.john.edge

Can you share the Rule on how to trigger hubitat with Linktap?
I created a virtual switch and set it as trigger and in action added a send http post. I am unsure how to enter the syntax. Should it be on the

'Enter body for POST' or
'Enter URL to send request to under Send http POST' in Rule Machine?

I set this up via node red and my node red flow triggers the actions

If you look at the bottom workflow, this triggers a 10 min watering cycle its called from hubitat using a virtual http switch (search the forums for that) but basically when switched on hits http://mynoderedserver/WaterGardenOn

This starts the workflow..

The first function authenticates:

return {payload: "username=YOURUSERNAME&apiKey=YOURAPI&gatewayId=YOURGATEWAYID&taplinkerId=YOURTAPLINKID&action=true&duration=1", headers: {"Content-Type":"application/x-www-form-urlencoded"}};

Next step:
Put it in to instance mode, which kicks off the default 10 min watering cycle
https://www.link-tap.com/api/activateInstantMode

I then put it back in to the default 7 day mode <- if you don’t do this then I found that it would just stay in the instant mode and not reset back to the scheduled mode again

https://www.link-tap.com/api/activateSevenDayMode

Below is a quick driver for the Link Tap device. You will need your API key from Link Tap Wireless Water Timer - LinkTap (link-tap.com) as well as you username (not email)

/**
*   
*   File: Taplink_Driver.groovy
*   Platform: Hubitat
*   Modification History:
*       Date       Who                   What
*
*
*  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
*  in compliance with the License. You may obtain a copy of the License at:
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
*  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
*  for the specific language governing permissions and limitations under the License.
*
*
*/
def version() {"v1.0"}

metadata {
    definition (name: "Linktap Driver", namespace: "comrad", author: "Lamar Dykes") {
        capability "Valve"
        attribute "valve", "enum", [ "open", "closed" ]
    }
    
    preferences {
        input("apiKey", "text", title: "API Key:", description: "Linktap API Key")
        input("userKey", "text", title: "User Key:", description: "Linktap User Key")
        input("gatewayID", "text", title: "Gateway ID:", description: "Linktap Gateway ID")
        input("taplinkerID", "text", title: "taplinkerID:", description: "Linktap Taplinker ID")
    }
}

def open() {
    sendEvent(name: "valve", value: "open", isStateChange: true)
    log.debug 'Turning on LinkTapper'
    tapLinker("true",20)
}

def close() {
    sendEvent(name: "valve", value: "closed", isStateChange: true)
    log.debug 'Turning Off LinkTapper'
    tapLinker("false",0)
}

def tapLinker(action,time){
    def postBody = [
        username: "$userKey",
        apiKey: "$apiKey",
        gatewayId: "$gatewayID",
        taplinkerId: "$taplinkerID",
        action: action,
        duration: time,
        eco: false,
        ecoOn: 1,
        ecoOff: 2,
    ]
    
    def params = [
        uri: "https://www.link-tap.com/api/activateInstantMode",
        contentType: "application/json",
        body: postBody,
        textParser: true
    ]
    
        try{
            httpPost(params){response ->
                if(response.status != 200) {
                    sendPush("ERROR: 'Linktap' received HTTP error ${response.status}. Check your keys!")
                    log.error "Received HTTP error ${response.status}. Check your keys!"
                }
                else {
                    log.debug "LinkTapper Activated Successfully ${response.status}"
                        success = true
                }
            }
        }
        catch (Exception e) {
            log.error "An invalid key was probably entered. Taplinker Server Returned: ${e}"
        } 

    return success
    
}

Thanks for the driver @comrad. I've just started poking around with the Link-tap API and came across this thread.

I noticed that when the "instant mode" is activated or de-activated it disables my watering schedule. I think that a POST to https://www.link-tap.com/api/activateIntervalMode will reactivate the schedule (in my case anyways). Do you think you could add this to the driver for after the instant-mode is started or stopped? Also it would be a nice feature to be able to define the amount of time in minutes for the instant mode watering to happen.

Thanks for your time! I appreciate you writing this driver.
Jamie

I had to change the code to use switch instead of valve to get it working. I dont know how to make it go back to your programming.

 /**
*   
*   File: Taplink_Driver.groovy
*   Platform: Hubitat
*   Modification History:
*       Date       Who                   What
*
*
*  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
*  in compliance with the License. You may obtain a copy of the License at:
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
*  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
*  for the specific language governing permissions and limitations under the License.
*
*
*/
def version() {"v1.0"}

metadata {
    definition (name: "Linktap Driver", namespace: "comrad", author: "Lamar Dykes") {
        capability "Valve"
        capability "Switch"
        attribute "valve", "enum", [ "open", "closed" ]
    }
    
    preferences {
        input("apiKey", "text", title: "API Key:", description: "Linktap API Key")
        input("userKey", "text", title: "User Key:", description: "Linktap User Key")
        input("gatewayID", "text", title: "Gateway ID:", description: "Linktap Gateway ID")
        input("taplinkerID", "text", title: "taplinkerID:", description: "Linktap Taplinker ID")
    }
}

def on() {
    open()
}

def off() {
    close()
}

def open() {
    sendEvent(name: "valve", value: "open", isStateChange: true)
    log.debug 'Turning on LinkTapper'
    tapLinker("true",6)
    sendEvent(name: "valve", value: "closed", isStateChange: true)
}

def close() {
    sendEvent(name: "valve", value: "closed", isStateChange: true)
    log.debug 'Turning Off LinkTapper'
    tapLinker("false",0)
}


def tapLinker(action,time){
    def postBody = [
        username: "$userKey",
        apiKey: "$apiKey",
        gatewayId: "$gatewayID",
        taplinkerId: "$taplinkerID",
        action: action,
        duration: time,
        eco: false,
        ecoOn: 1,
        ecoOff: 2,
    ]
    
    def params = [
        uri: "https://www.link-tap.com/api/activateInstantMode",
        contentType: "application/json",
        body: postBody,
        textParser: true
    ]
    
        try{
            httpPost(params){response ->
                if(response.status != 200) {
                    sendPush("ERROR: 'Linktap' received HTTP error ${response.status}. Check your keys!")
                    log.error "Received HTTP error ${response.status}. Check your keys!"
                }
                else {
                    log.debug "LinkTapper Activated Successfully ${response.status}"
                        success = true
                }
            }
        }
        catch (Exception e) {
            log.error "An invalid key was probably entered. Taplinker Server Returned: ${e}"
        } 

    return success
    
}

Thanks @comrad, I'll give this a try. I'm not going to have access to my Hubitat for a couple weeks, but as soon as I do I'll give it a shot.

Cheers!

For Linktap owners, I did my own driver for the Tap Linker product, should you want to try. It includes the ability to receive events/data from Linktap, apart from start/stop watering. More info here

Cheerse

1 Like