YoLink Integration

BTW, related, any news on Z-Wave LR?

@steve.hoge thanks I'm new to Hubitat and Groovy (like yesterday new :smile: ) but gave this a try based on your Python code (I love Python). This Driver so far will get the access token after you enter in your UAID and Secret Key, save, then press configure. This will also then populate the devicesList state variable after you refresh. Then you can plugin the deviceToken and deviceID in the preferences and save, and now use the refresh button to pull those state variables from the sensor. Note I'm using this with a motion sensor, so be sure to change the part to your device type if not using MotionSenor in the getMotionSensorDetails command and also the updateState logic to reflect your state variables.

I don't quite know how to setup polling or use the HTTP Callback API for YoLink I see mentioned on the API docs so that the state would update based on a motion event. Sorry if it is a little rough, still learning everything but wanted to share in case it helps anyone else get closer to something working.

/*
 * YoLink API
 *
 * Talks to the YoLink API using user credentials from mobile app
 * Based off Python code by @steve.hoge and using snippets of various project examples including: https://github.com/hubitat/HubitatPublic/tree/master/examples/drivers
 * 
 */
metadata {
    definition(name: "YoLink API", namespace: "community", author: "Dev Dre4ms") {
        capability "Configuration"
        command "refresh"
        
        attribute "access_token", "text"
        attribute "deviceList", "text"
       
        attribute "alertInterval", "text"
        attribute "battery", "text"
        attribute "devTemperature", "text"
        attribute "ledAlarm", "text"
        attribute "nomotionDelay", "text"
        attribute "sensitivity", "text"
        attribute "state", "text"
        attribute "lastMotion", "text"
        
    }
}

preferences
{
    section
    {
        input "uaid", "text", title: "YoLink UAID", required: true
        input "secret", "password", title: "YoLink Secret Key", required: true
        input "deviceID", "text", title: "Device ID", required: false
        input "deviceToken", "text", title: "Device Token", required: false
        input name: "logEnable", type: "bool", title: "Enable debug logging", defaultValue: false
    }
}


def refresh() {
    def motionState = getMotionSensorDetails(state.access_token, deviceToken, deviceID)
    updateState(motionState)
    logDebug("Motion state status:${motionState}")
}

def updateState(Map motionState)
{
    state.alertInterval = motionState.data.state.alertInterval
    state.battery = motionState.data.state.battery
    state.devTemperature = motionState.data.state.devTemperature
    state.ledAlarm = motionState.data.state.ledAlarm
    state.nomotionDelay = motionState.data.state.nomotionDelay
    state.sensitivity = motionState.data.state.sensitivity
    state.state = motionState.data.state.state
    state.lastMotion = motionState.data.reportAt
    
    
}

def logDebug(msg) 
{
    if (logEnable)
    {
        log.debug(msg)
    }
}


def configure()
{
    getAccessToken()

    //Get devicelist and update attribute to see deviceToken/deviceId
    def command_response = post_command(state.access_token, "Home.getDeviceList", [:])
    logDebug("Devices List: ${command_response}")
    state.devicesList = command_response
    
}

def getAccessToken()
{
    def postParams = genParamsPre()
    postParams['body'] = ["grant_type": "client_credentials"]
    try
    {
        token_response = httpPostExec(postParams, false)      
        logDebug("Received access token: ${token_response.access_token}")
        state.access_token = token_response.access_token
        
        return token_response.access_token
    }
    catch (Exception e)
    {
        logDebug("Error retrieving access token from YoLinkAPI")
    }
    
}

def getTokenURI()
{
    return "https://api.yosmart.com/open/yolink/token"
}

def getAPIURI()
{
    return "https://api.yosmart.com/open/yolink/v2/api"
}

def genParamsPre()
{
    def params =
        [
            uri: getTokenURI(),
            headers:
            [
                'Authorization': "Basic " + ("${uaid}:${secret}").bytes.encodeBase64().toString()
            ],
            contentType: 'application/json'
        ]
 
    return params
}

def genCommandParams(String token)
{
    def params =
        [
            uri: getAPIURI(),
            headers:
            [
                'Authorization': "Bearer " + token
            ],
            contentType: 'application/json'
        ]
 
    return params
}

def httpPostExec(params, throwToCaller = false)
{
    logDebug("httpPostExec(${params})")
    def response = [:]
    try
    {
        httpPost(params) { resp ->
            if (resp.success)
            {
                
                response = resp.getData()
                logDebug("Success: ${response}")
            }
            
        }
        
    }
    catch (Exception e)
    {
        logDebug("httpPostExec() failed: ${e.message}")
        if(throwToCaller)
        {
            throw(e)
        }
    }
    return response
}

def getCurrentTime() {
    now = new Date()
    return now.getTime()
}

def post_command(String token, String method, Map methodData) {
	def params = genCommandParams(token)

	def commandData = methodData
	commandData['method'] = method
    commandData['time'] = getCurrentTime().toString()
    params["body"] = commandData
    
	return httpPostExec(params, true)
}

def getMotionSensorDetails(String token, String deviceToken, String deviceID)
{
    //This needs to be updated for the type of sensor you'd like to get the state from, replace MotionSensor.getState with your device type
    def deviceData = post_command(token, 'MotionSensor.getState', [
    'token': deviceToken,
    'targetDevice': deviceID,
    'params': [:]])
}

def logsOff() {
    log.warn "debug logging disabled..."
    device.updateSetting("logEnable", [value: "false", type: "bool"])
}
1 Like

Very cool! :smiley: I got it basically installed and talking to my Hub and it's listing my devices!

But I don't have a motion sensor and don't know enough about Groovy drivers yet to exactly know how to modify this based on querying my THSensor and my DoorSensor, so it's throwing the expected errors until I can modify it to talk to them.

Thanks for including the link to HubitatPublic/examples/drivers at master · hubitat/HubitatPublic · GitHub , I will be perusing that to try and understand Groovy driver semantics and control flow.

Google App Script to read temperature Sensor and better understand how the API works.

Enjoy !,  James


   // Google Apps Script
  // coded by James Wiser version 2-21-22 contact james@wisermail.com

function myFunction() {
  
  // console.logs are included to help you gain clarity in what is going on
  
  // API Reference Guide http://doc.yosmart.com/docs/overall/intro
  // yolinkURL='https://api.yosmart.com/open/yolink/v2/api' 
  // tokenURL = "https://api.yosmart.com/open/yolink/token"
 
  // client_id and client_secret are obtained from the app on your phone.  keep these private
  var client_id = 'ua_********************************';
  var client_secret = 'sec_***************************';
  
  var access_token_url = 'https://api.yosmart.com/open/yolink/token';
  var yolinkurl = "https://api.yosmart.com/open/yolink/v2/api";

  
  // authentication to get token from Yolink
  var data = {
    'grant_type' : 'client_credentials', 
   };

  var options = {
    'method': 'post',
    'payload': data,
    'headers': {
      'Authorization': 'Basic ' + Utilities.base64Encode(`${client_id}:${client_secret}`),
    },
  };
  var resp = UrlFetchApp.fetch(access_token_url, options);
  
  console.log(JSON.parse(resp.getContentText()));
  var data = JSON.parse(resp.getContentText());
  var token = data['access_token'];
  console.log(token);

// call API to get a valid list of devices
var now = new Date();
var timems = now.getTime();
console.log(timems);

// payload elements can be referenced in API Reference Guide
var payload = {
            
            "time": timems,
            "method": "Home.getDeviceList",
            
        }; 


// use the token that Yolink gave you
var options = {
    "method": "post",
     "Content-Type": "application/json",
     "headers": {'Authorization': 'Bearer '+  token},
     "payload" : payload  
    }


  var resp2 = UrlFetchApp.fetch(yolinkurl, options);
  
  console.log(JSON.parse(resp2.getContentText()));
  
  var respdata = JSON.parse(resp2.getContentText());
  var yolinkdata = respdata['data'];
  var devicelist = respdata['data']['devices'];
  // 0 id the 1st device in the data set, 1 would be the next and so on...
  var device = respdata['data']['devices'][0].deviceId;
    
  console.log(yolinkdata);
  console.log(devicelist);
  console.log(device);

  // find the first device that is a tempreature sensor
  var numofdevices = devicelist.length;
  for (var counter = 0; counter < numofdevices; counter = counter + 1) {
    console.log(respdata['data']['devices'][counter].deviceId);
    // type comes from API Reference Guide
    if (respdata['data']['devices'][counter].type == 'THSensor') {
        var ThSensor_token  = respdata['data']['devices'][counter].token;
        var ThSensor_deviceId  = respdata['data']['devices'][counter].deviceId;
        var TheSensor_name = respdata['data']['devices'][counter].name;
        break;
    }   
  }
  
 // format the next request with the required elements for the device. refer to API Reference Guide
 var payload = {
            
            "time": timems,
            "method": "THSensor.getState",
            "targetDevice": ThSensor_deviceId,
            "token" : ThSensor_token,
            
        }; 


var options = {
    "method": "post",
     "Content-Type": "application/json",
     "headers": {'Authorization': 'Bearer '+  token},
     "payload" : payload  
    }


  var resp3 = UrlFetchApp.fetch(yolinkurl, options);
  console.log(JSON.parse(resp3.getContentText()));
  
  var respdata = JSON.parse(resp3.getContentText());

  var tempsens = respdata['data']['state'].temperature
  var tempmode = respdata['data']['state'].mode
  var TheSensor_ts = respdata['data']['reportAt'];
  if (tempmode == 'f' ){
     temp = (tempsens * 9/5) + 32;
     temp = temp.toFixed(1);
  }
 else{
      temp = temp.toFixed(1);
      tempmode = 'c';
     }
  
  // use the date/time stamp that comes from the data set to accurately report the last reading
  var date = new Date(TheSensor_ts);
  // use the timezone you are working in
  var fdate = Utilities.formatDate(date, 'America/Chicago','MM-dd-yy hh:mm:ss aa');
  
  console.log(TheSensor_name + " is " +  temp + " " + tempmode + " " + fdate);

   
  }


2 Likes

They claim they will come out with an API in 2022.

If they come out with a LOCAL API, I'd be all over their devices. If cloud... Not so much.

Hi @steve.hoge,
Any further progress on the cloud API integration with Hubitat? I realize that the current API exposed by Yolink is cloud based, but for now, I am OK with this at this point unlike some here in the community. Even if cloud based, an integration app for noob users like me (non-coders) which could be uploaded like the Ecobee Hubitat cloud integration would be fantastic! Thanks for your contributions to this effort!

Have taken a break from Hubitat projects for awhile but will get back to it eventually. I was kind of hoping based on the proof-of-concept prototypes here that @Hubitat_Staff might pick up the YoLink ball and run with it :wink:

Yes, was hoping that Hubitat staff would pick up on this as well but I think that they are philosophically set on local only integrations (which I understand) and so are relying on community based integrations for anything that has to access API that are cloud based. Anyway, thanks for all your efforts and let us all know if (and hopefully not just if, but when) you decide to develop this further. Thanks again.

1 Like

Anyone have a good Z-wave water thermostat solution that integrates with Hubitat. Need it to monitor lake temperature in the summer. Probably installing it under my dock.

2 Likes

Any news on yolink and their cloud API? Is anyone working on a cloud integration driver for HE?

I don't think so...

1 Like

Integrate your YoLink devices with Home Assistant, because YoLink has created a cloud integration for HA.

There is an excellent Home Assistant integration for Hubitat called Home Assistant Device Bridge, linked to below, that permits Home Assistant entities to be available as Hubitat devices.

Alright, I was hoping to not have to do this but I'll spin up a HA machine for this. I also have some other sensors that seem to only work with HA. Thank you for the pointer!

1 Like

There are many of us who integrate “incompatible” devices into Hubitat using other hubs. In the instance of HA, the integration is local (both hubs are on the same LAN), and therefore fast enough that there’s no noticeable effect on automation speed.

1 Like
3 Likes

Just saw your post -- I've been thinking about this to monitor lake temperature close to the freeze point to trigger my bubbler to turn on so that I don't get a freeze in front of the wall. But, a benefit would be lake temperature in the summer!

I'm not a programmer. If Yolink can report to my cell phone in an instant, why can't they take that info and use it in Hubitat? I have a motion sensor in my mail box and my cell phone buzzes before I get the door shut after opening it to get the mail out.

There is already a community-contributed YoLink integration that probably does what you need:

The only "deficiency" is that it is cloud-based, connecting HE to your YoLink devices through an API running on YoLink's servers, so is possibly a little slower than desired and obviously dependent on their servers and a functioning internet connection. In contrast, Hubitat prefers to officially support solutions that run entirely on your local LAN, but YoLink has not yet released an API that would allow us to connect directly to our YoLink hubs.

Supposedly YoLink will come out with a new hub that supports a local API this year. Disclaimer - they also said it would come out last year too, so caveat emptor.

They make nice products, so I hope they do follow through with a local API at some point as I would never hinge anything important in home automation on a cloud integration solution.

1 Like

Download the Hubitat app