SkyDrop Sprinkler Controller

Hi, Has anyone considered making an add-in to connect to a Skydrop sprinkler controller?

You may want to contact the seller / manufacturer to see if they could write a wifi component to connect to Home Assistant. They have a vested interest in making it compatible with HA.

You would likely need documentation from them ti write the component.

I did email tech support,but they don’t understand. They have the watering time coming in Nest reports now and it does connect via IFTT, they see no need for more.

I did send some time searching online but nothing that would help with SkyDrop.

Found a component for OpenSprinkler ( actual have an old model that I have never hooked up) that was updated last month. OpenSprinkler Component.

Amazon had the price for SkyDrop from about $90 to Over $400. A little more than I want to pay for something to play with.

I think that I could control a Pi or ESP with NodeRed and do about all the watering from inside of Home-Assistant that I need to do. i.e. Time of day, Day of the week and rain cutout. (county doesn’t allow watering on certain days or if it is raining outside and only in the morning or at night).

I agree that SkyDrop isn’t required, but I had it before I started with Hassio. A couple of Sonofff 4 CH Pro with tasmota firmware and Big Timer in the Node Red add-on would do the job.

I know this is old but I just finished linking my Skydrop system to openhab and node red - with the end goal of letting me turn on/off and see the status of my sprinklers in HomeKit. I’m pleased with how it’s working and would be happy to share the code with you.

Necessary first steps: email [email protected] asking for developer access to your account. Then when you have access you’ll need to set up oauth2 authentication to get access tokens. Finally calls are made to Skydrop with get and post http calls.

If home assistant can do oauth2 then you should be able to do the rest with simple http calls.

Hi, I see you connected Skdrop to your home automation. Could you share the code? I may not be adept enough to use it myself with Hassio, but do have access to people that are at work. Did you use Node Red with dashboard?

I’m using openhab for the oauth2 authentication and node red to make api calls. I use homekit exclusively for my user interface. I will put a post together showing how I’ve done everything.

There’s no reason you can’t adapt to run the GET and POST http calls through home assistant- I was doing this to link sprinklers to HomeKit so I choose node red since I like how it interacts with homekit.

I’ll try to put together the “important” parts that you’ll need to adapt to your setup. The first step as I said before is to get your API access - then you can get your API key and consumer secret. This will be used for the OAUTH2 authentication. This is a cloud - to - cloud authentication between (openhab in my case) and skydrop. My HTML file for accomplishing this is:

<!DOCTYPE html>
<html>
        <head>
                <title>OAuth2 Catcher</title>
        <script type="text/javascript">
        function getAllUrlParams(url) {

  // get query string from url (optional) or window
  var queryString = url ? url.split('?')[1] : window.location.search.slice(1);

  // we'll store the parameters here
  var obj = {};

  // if query string exists
  if (queryString) {

    // stuff after # is not part of query string, so get rid of it
    queryString = queryString.split('#')[0];

    // split our query string into its component parts
    var arr = queryString.split('&');

    for (var i=0; i<arr.length; i++) {
      // separate the keys and the values
      var a = arr[i].split('=');

      // in case params look like: list[]=thing1&list[]=thing2
      var paramNum = undefined;
      var paramName = a[0].replace(/\[\d*\]/, function(v) {
        paramNum = v.slice(1,-1);
        return '';
      });

      // set parameter value (use 'true' if empty)
      var paramValue = typeof(a[1])==='undefined' ? true : a[1];

      // (optional) keep case consistent
      paramName = paramName.toLowerCase();
      paramValue = paramValue.toLowerCase();

      // if parameter name already exists
      if (obj[paramName]) {
        // convert value to array (if still string)
        if (typeof obj[paramName] === 'string') {
          obj[paramName] = [obj[paramName]];
        }
        // if no array index number specified...
        if (typeof paramNum === 'undefined') {
          // put the value on the end of the array
          obj[paramName].push(paramValue);
        }
        // if array index number specified...
        else {
          // put the value at that index number
          obj[paramName][paramNum] = paramValue;
        }
      }
      // if param name doesn't exist yet, set it
      else {
        obj[paramName] = paramValue;
      }
    }
  }

  return obj;
}
var code = getAllUrlParams().code;
        </script>
        </head>
<script>
var item = 'Skydrop_AuthCode'
document.write("Preparing to update " + item + " Item...")
var code = getAllUrlParams().code;
var xhr = new XMLHttpRequest();
xhr.open('POST', "https://myopenhab.org/rest/items/"+item);
xhr.setRequestHeader("Content-Type", "text/plain");
xhr.setRequestHeader("Accept", "application/json");
xhr.onreadystatechange = function() {
        if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
                document.write("Received  " + code);
        }
        else if(xhr.status != 200) {
                document.write("Error in callback: " + xhr.status + " " + xhr.responseText);
        }
}
document.write("Sent " + code + "...");
xhr.send(code)
</script>

</html>

Next up you need to make what I refer to as my “magic link” which starts the authentication process.

https://api.skydrop.com/oauth/authorize?client_id=***YOUR_CLIENT_ID***&redirect_uri=https://myopenhab.org/static/oauth2.html&response_type=id_token

This will send back an auth code which expires quite quickly - so I have the rules below that take the auth code and turn it into my first set of access and refresh tokens:

import java.net.URL
import javax.net.ssl.HttpsURLConnection
import java.io.BufferedInputStream
import java.io.BufferedReader
import java.io.InputStreamReader
import org.eclipse.xtext.xbase.lib.Procedures
import org.eclipse.xtext.xbase.lib.Functions

val LOG = "skydrop"

// Constants
val BASE_URL = "https://api.skydrop.com" // change to the URL for your service
val TOKEN        = "/oauth/token" // change this to the endpoint URL for your service

// Takes an HTTPS connection and reads the response data. If the response code is not 200 the String
// "ERROR <code>" is returned where <code> is the HTTP return code. 
// This is a lambda that gets reused for all the API calls.
val Functions$Function1<HttpsURLConnection, String> readResponse = [ connection |

  logInfo("skydrop", "checking response code")
  val responseCode = connection.responseCode
  if(responseCode != 200){
    logError("skydrop", "Received non-ok return code: " + responseCode)
    return "ERROR " + responseCode.toString;
  }

  logInfo("skydrop", "pulling response")
  val StringBuffer sb = new StringBuffer()
  val BufferedReader br = new BufferedReader(new InputStreamReader(new BufferedInputStream(connection.getInputStream())))
  var String inputLine = ""
  while ((inputLine = br.readLine()) !== null) {
    sb.append(inputLine)
  }
  sb.toString
]

// Acquires a new auth token when a new auth code is retrieved or an auth token expires
val Procedures$Procedure4<String, String, Boolean, Functions$Function1<HttpsURLConnection, String>> getAuth = [ tokenEndpoint, code, refresh, readResp | 
  // Change these to your values
  val clientId = "YOUR_CLIENT_ID"
  val clientSecret = "YOUR_CLIENT_SECRET"
  val redirect = "https://myopenhab.org/static/oauth2.html"

  // Prepare the Request
  var request = 'client_secret='+clientSecret+'&client_id='+clientId+'&{code}&grant_type={type}&redirect_uri='+redirect // the request for your service may be different
  if(refresh) request = request.replace('{code}', '&refresh_token='+code).replace('{type}', 'refresh_token')
  else        request = request.replace('{code}', '&code='+code).replace('{type}', 'authorization_code')

  val tokenURL = new URL(tokenEndpoint)
  val HttpsURLConnection connection = tokenURL.openConnection() as HttpsURLConnection

  // Set the headers and parameters
  connection.setRequestProperty('content-type', 'application/x-www-form-urlencoded')
  connection.setRequestProperty('cache-control', 'no-cache')
  connection.requestMethod = "POST"
  connection.doOutput = true
  connection.setDoInput = true

  // Make the HTTPS call
  logInfo("skydrop", "Attempting to " + if(refresh) "refresh " else "acquire " + "auth tokens")
  connection.outputStream.write(request.getBytes("UTF-8"))

  // Get the response body
  logInfo("skydrop", "Reading response")
  val response = readResp.apply(connection)
  if(response.startsWith("ERROR")) {
    logError("skydrop", "Failed to " + if(refresh) "refresh" else "obtain" + "auth token")
    return;
  }

  // Populate the token Items
  logInfo("skydrop", "Populating auth token Items")
  Skydrop_AccessToken.postUpdate(transform("JSONPATH", "$.access_token", response))
  Skydrop_RefreshToken.postUpdate(transform("JSONPATH", "$.refresh_token", response))
  Skydrop_Expires.postUpdate(now.plusSeconds(Integer::parseInt(transform("JSONPATH", "$.expires_in", response))).toString)

  // Poll Skydrop for the latest data
  Skydrop_Poll.sendCommand(ON)

]

// Use the AuthCode to get the auth and refresh tokens
rule "Received authcode"
when
  Item Skydrop_AuthCode received command
then
  getAuth.apply(BASE_URL+TOKEN, receivedCommand.toString, false, readResponse)
end

// Use the refresh token to get a new auth and refresh token
rule "Refresh the auth token"
when
  Item Skydrop_Refresh received command ON
then
  getAuth.apply(BASE_URL+TOKEN, Skydrop_RefreshToken.state.toString, true, readResponse)
end

Up to this point I’ve just copied code from someone else in openhab. I’m sure someone over here at home-assistant has a solution for oauth2 authentication through the home-assistant servers. Note that rule also has a “refresh token” script since the access token expires every 24 hours I just run this 3 times a day (in case my Pi is turned off during one of the times).

Once I’ve completed those steps I’m ready to move over to node-red. My node red checks the watering status every 5 minutes when I’m “not supposed to be watering” and every 1 minute when I’m supposed to be watering. Additionally there’s a 15-second loop that updates states every 15 seconds during watering.

Essentially I do everything with 4 API calls:

  • One time “get controller ID”
  • Check state of each zone
  • Water zone (zone number)
  • Stop watering

api.skydrop.com has good instructions and documentation on how to run all of the calls. If you think it’d be helpful I can share what’s inside of the functions… This is what my node-red flow looks like:

The main loop is up at the top with my 1 and 5 minute polls along with my “POST” and “Poll Status” (GET) HTTP nodes. These loops run all the time and keep my homekit system updated within about 1 minute of what’s actually happening in the yard.

The part with “startup read” and file read/writes is to keep my access and refresh tokens saved across reboots and updated in node-red to a global variable. This way I can pull the current access token whenever I run a function to put together an API call.

Feel free to ask more questions! I’m always happy to feel helpful to other people out there, mostly since I’ve copied so much code from the open source world into my own setup…

Thank you very much! I’ve got some learning to do before I ask what is in the functions.

Cheers

The functions mostly hold on to my tokens and put together the URL for new requests.

I’d recommend you start with api.skydrop.com - they have good instructions on how to run the initial authentication, how to renew tokens, and how to put together API requests. I put my whole system together by copying the above script from OpenHAB forums - it was originally written for the Dexcom API, I just changed every mention of “dexcom” to the proper “skydrop” addresses and entered my keys.

From there you should be able to search the Home-Assistant forums for “oauth2 authentication” or something similar to see how to go about that process.

Once you’ve done the original authentication you’re through the hard part, from there it’s just formulating your API calls (HTTP addressed GET and POST commands) and making your rules how you like.

Have fun!

What is the green node?

Green ones are openhab - they save my auth tokens as strings in my openhab. Openhab does the oauth2 work.

I don’t know if anyone is still using these controllers, but I have had one for quite a while and I just got another one for $25 (! cheaper than a 4ch sonoff) for my backyard.

I’ve whipped up a python library for the API:

and I intend to get a HA integration going in the next couple weeks. I’ll keep this thread updated.

Wow, better late than never! I replaced mine with a LinkNode R8, esphome and NodeRed last year but still have the SkyDrop. I didn’t like when they became apaid subscription.

Hey all, the integration is finished:

Check it out!

1 Like