Sensibo API Integration - Based on Rules

I double-check my running version and there’s no space, so I don’t think that’s the issue.

I had a similar problem a couple weeks ago when one of my items hadn’t been initialized properly. If you take a look at the latest version on Github, I’ve made a few more tweaks to improve the error handling and logging to help with troubleshooting.

Newbie here. I’m having problem with transform(“JSONPATH”, “$.result[0].id”, PodsResult). It returns the original values stored in PodsResult. Any idea why?

Update:
Found the problem. I did not install the JsonPath transformation service in OpenHAB.

1 Like

Hello there,

I’m trying to use the Integration rules on a Windows 10 OH2 with a Sensibo Sky and I’m not getting much success. I can get valid responses from the API when I post the https or when I use CURL but nothing gets updated in OH2 and the pod is unresponsive to commands.

In the openhab.log I get the following errors:

2018-05-05 07:39:25.853 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"cool","fanLevel":"auto","swing":"stopped","targetTemperature":23}}
2018-05-05 07:39:27.334 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 07:39:32.351 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"cool","fanLevel":"auto","swing":"stopped","targetTemperature":23}}
2018-05-05 07:39:34.073 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 07:39:39.081 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"cool","fanLevel":"auto","swing":"stopped","targetTemperature":23}}
2018-05-05 07:39:40.610 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 07:39:45.614 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"cool","fanLevel":"auto","swing":"stopped","targetTemperature":23}}
2018-05-05 07:39:47.229 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 07:39:52.242 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"cool","fanLevel":"auto","swing":"stopped","targetTemperature":23}}
2018-05-05 07:39:54.084 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 07:39:59.101 [ERROR] [smarthome.model.script.Sensibo write] - 5 failures updating Sensibo

I copied the rules, items and sitemap pretty much verbatim from github so I’m at loss to understand what went wrong and would appreciate a bit of help.
TIA

Can you try it without the “swing” property? It looks like the API might have changed, and that property isn’t included in the full state update anymore. If you want to change a single property, it looks like you now use a PATCH call instead.

Let me know if that works for you and I’ll change up the code.

Sure I’ll try it . How do you disable just the swing property in the rules?

Just change

 var String CommandData = CommandState + CommandMode + CommandFan + CommandSwing + CommandTemp

to

 var String CommandData = CommandState + CommandMode + CommandFan + CommandTemp

Right, I edited line 142 in your rules and took the CommandSwing out. It didn’t change much because the openhab.log immediately recorded the following:

2018-05-05 21:32:54.803 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'sensibo.rules'
2018-05-05 21:33:23.451 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"auto","fanLevel":"auto","targetTemperature":23}}
2018-05-05 21:33:25.018 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 21:33:30.028 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"auto","fanLevel":"auto","targetTemperature":23}}
2018-05-05 21:33:31.599 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 21:33:36.606 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"auto","fanLevel":"auto","targetTemperature":23}}
2018-05-05 21:33:38.228 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 21:33:43.236 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"auto","fanLevel":"auto","targetTemperature":23}}
2018-05-05 21:33:44.602 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 21:33:49.612 [INFO ] [arthome.model.script.Sensibo command] - {"acState":{"on":true,"mode":"auto","fanLevel":"auto","targetTemperature":23}}
2018-05-05 21:33:51.375 [INFO ] [marthome.model.script.Sensibo result] - <html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>
2018-05-05 21:33:56.389 [ERROR] [smarthome.model.script.Sensibo write] - 5 failures updating Sensibo

So it didn’t work but I’m open to try other suggestions.
TIA
Claude

I’m not sure, it looks fine to me, and the same data is working on my end. Maybe reach out to Sensibo to see if they can tell you what it looks like in their logs. If you give them the pod ID and the time, they should be able to track it down.

Hmmpff…they don’t like me a lot there. I’ll see how it develop.

Hi everyone,

Has anyone been able to get this working with two pods? My understanding is I would need only one API key, but how do you differentiate between the two pods? Is there a way to get a Pod ID for each and if so, where do you put that information in the .rules file?

Hi Ewan,
I have been able to get this working with three pods.

This rule below to get Pod ID for each Sensibo :

val String APIKey = "your_api_key"

var PodID_01 = ""  
var PodID_02 = ""
var PodID_03 = ""

/*** Read Pod ID ***/
rule "Read Pod ID"
when
    System started
then
  try {          
    var String PodsResult
  
    do {
      PodsResult = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/users/me/pods?apiKey=' + APIKey + '"', 10000)
    } while (!PodsResult.contains('"status": "success"'))

    PodID_01 = transform("JSONPATH", "$.result[0].id", PodsResult)   
    PodID_02 = transform("JSONPATH", "$.result[1].id", PodsResult)
    PodID_03 = transform("JSONPATH", "$.result[3].id", PodsResult)
   
    // etc ... PodID_n = transform("JSONPATH", "$.result[n-1].id", PodsResult)
  }
  catch(Throwable t) {
    logError("ReadPodID", "Error was caught: {}", t)
  }
end

Thanks Kevin,

What changes did you then make for the your sensibo.items to differentiate between the three pods?

This is my sensibo.items :

Switch Sensibo_Living_State  ["Switchable"]
Number Sensibo_Living_Temp
Number Sensibo_Living_Humidity
Number Sensibo_Living_Battery
Number Sensibo_Living_Target
String Sensibo_Living_Fan
String Sensibo_Living_Mode
String Sensibo_Living_Swing

Switch Sensibo_Bedroom_State  ["Switchable"]
Number Sensibo_Bedroom_Temp
Number Sensibo_Bedroom_Humidity
Number Sensibo_Bedroom_Battery
Number Sensibo_Bedroom_Target
String Sensibo_Bedroom_Fan
String Sensibo_Bedroom_Mode
String Sensibo_Bedroom_Swing

Switch Sensibo_Kitchen_State  ["Switchable"]
Number Sensibo_Kitchen_Temp
Number Sensibo_Kitchen_Humidity
Number Sensibo_Kitchen_Battery
Number Sensibo_Kitchen_Target
String Sensibo_Kitchen_Fan
String Sensibo_Kitchen_Mode
String Sensibo_Kitchen_Swing

And follow this link to make changes of each Sensibo

It works perfectly - thank you all for your help.
One question: if i want to start cooler at a certain temperature under some condition - let’s say to keep things simple start at 11.00 am - how can i do that? Tnx

Ken, would you mind pasting your entire sensibo-interface.rules? I’m getting this error:

2018-06-04 16:55:00.034 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'Read Sensibo Measurements': The name 'PodID' cannot be resolved to an item or type; line 88, column 128, length 5

you should post your sensibo-interface.rules and your sensibo.items. I’ll debug for you

My rules:

val String APIKey = "Redacted"

var PodID_01 = ""  
var PodID_02 = ""

/*** Read Pod ID ***/

rule "Read Pod ID"
when
    System started
then
  try {          
    var String PodsResult
  
    do {
      PodsResult = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/users/me/pods?apiKey=' + APIKey + '"', 10000)
    } while (!PodsResult.contains('"status": "success"'))

    PodID_01 = transform("JSONPATH", "$.result[0].id", PodsResult)   
    PodID_02 = transform("JSONPATH", "$.result[1].id", PodsResult)
    
    // etc ... PodID_n = transform("JSONPATH", "$.result[n-1].id", PodsResult)
  }
  catch(Throwable t) {
    logError("ReadPodID", "Error was caught: {}", t)
  }
end

/*** Read Sensibo State PodID01***/

rule "Read Sensibo State"

  when
    System started
  then

  Thread::sleep(10000)

  try {
    var String PodStatus 
      
    do {
      Thread::sleep(5000)
      PodStatus = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/pods/' + PodID + '/acStates?apiKey=' + APIKey + '&limit=1&fields=acState"', 5000)
    } while (!PodStatus.contains('"status": "success"'))
    
    val String PodOn = (transform("JSONPATH", "$.result[0].acState.on", PodStatus))
    val String PodMode = (transform("JSONPATH", "$.result[0].acState.mode", PodStatus))
    val Number PodTarget = new Integer(transform("JSONPATH", "$.result[0].acState.targetTemperature", PodStatus))
    val String PodFan = (transform("JSONPATH", "$.result[0].acState.fanLevel", PodStatus))

    if (PodOn == "true")
      { postUpdate(SensiboState, ON) }

    if (PodOn == "false")
      { postUpdate(SensiboState, OFF) }

    postUpdate(SensiboTarget, PodTarget)
    postUpdate(SensiboMode, PodMode)
    postUpdate(SensiboFan, PodFan)

    if (PodStatus.contains('"swing": ')) {
      val String PodSwing = (transform("JSONPATH", "$.result[0].acState.swing", PodStatus))
      postUpdate(SensiboSwing, PodSwing)
    }
  }

  catch(Throwable t) {
    logError("Sensibo read state", "Error was caught: {}", t)
  }

end
  

/*** Read Sensibo Measurements ***/

rule "Read Sensibo Measurements"

  when
    Time cron "0 0/5 * * * ?"
  then

  try {
    var String PodMeasurements
      
    do {
      PodMeasurements = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/pods/' + PodID + '/measurements?apiKey=' + APIKey + '&fields=temperature,humidity,batteryVoltage"', 5000)
      Thread::sleep(5000)
    } while (!PodMeasurements.contains('"status": "success"'))
        
    val Number PodTemperature = new Double(transform("JSONPATH", "$.result[0].temperature", PodMeasurements))
    val Number PodHumidity = new Double(transform("JSONPATH", "$.result[0].humidity", PodMeasurements))

    postUpdate(SensiboTemp, PodTemperature)
    postUpdate(SensiboHumidity, PodHumidity)

    if (!PodMeasurements.contains('"batteryVoltage": null')) {
      val Number PodBattery = new Integer(transform("JSONPATH", "$.result[0].batteryVoltage", PodMeasurements))
      postUpdate(SensiboBattery, PodBattery)
    }
  }
  
  catch(Throwable e) {
    logError("Sensibo measurements", "Error was caught: {}", e)
  }

end

/*** Write Sensibo State ***/

rule "Write Sensibo State"

  when
    Item SensiboState changed or
    Item SensiboMode received command or
    Item SensiboFan received command or
    Item SensiboSwing received command or
    Item SensiboTarget received command
  then
  
  try {

    Thread::sleep(1000)         // Avoid race condition where state has not yet been updated

    var Boolean PodBoolean

    if (SensiboState.state == ON)
      { PodBoolean = true }
    else
      { PodBoolean = false }

    var String CommandURL = 'https://home.sensibo.com/api/v2/pods/' + PodID + '/acStates?apiKey=' + APIKey

    var String CommandState = '{"acState":{"on":' + PodBoolean + ','
    var String CommandMode = '"mode":"' + SensiboMode.state + '",'
    var String CommandFan = '"fanLevel":"' + SensiboFan.state + '",'
    var String CommandSwing = ""

    if (SensiboSwing.state != "")
      { CommandSwing = '"swing":"' + SensiboSwing.state + '",' }

    var String CommandTemp = '"targetTemperature":' + (SensiboTarget.state as DecimalType).intValue + '}}'
    var String CommandData = CommandState + CommandMode + CommandFan + CommandSwing + CommandTemp

    var String UpdateResult
    var Number Attempts = 0
  
    do {
      Attempts += 1
      logInfo("Sensibo command", CommandData)
      UpdateResult = executeCommandLine('curl@@-sSH@@"Content-Type: application/json"@@-XPOST@@' + CommandURL + '@@-d@@'+ CommandData, 10000)
      logInfo("Sensibo result", UpdateResult)
      Thread::sleep(5000)
    } while (!UpdateResult.contains('"status": "success"') && Attempts < 5)
  
    if (Attempts == 5) {
      logError("Sensibo write", "5 failures updating Sensibo")
    }      
  }

  catch(Throwable t) {
    logError("Sensibo write", "Error was caught: {}", t)
  }

end

My items:

/* Sensibo */

Switch Sensibo_Living_State  ["Switchable"]
Number Sensibo_Living_Temp
Number Sensibo_Living_Humidity
Number Sensibo_Living_Battery
Number Sensibo_Living_Target
String Sensibo_Living_Fan
String Sensibo_Living_Mode
String Sensibo_Living_Swing

Switch Sensibo_Bedroom_State  ["Switchable"]
Number Sensibo_Bedroom_Temp
Number Sensibo_Bedroom_Humidity
Number Sensibo_Bedroom_Battery
Number Sensibo_Bedroom_Target
String Sensibo_Bedroom_Fan
String Sensibo_Bedroom_Mode
String Sensibo_Bedroom_Swing

@EBF
You test with PodID_01, so you must change the PodID to PodID_01 in your rules.
And your items is Sensibo_***_***, so you must change to that items

rule "Read Sensibo State  PodID_01"

  when
    System started
  then

  Thread::sleep(10000)

  try {
    var String PodStatus 
      
    do {
      Thread::sleep(5000)
      PodStatus = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/pods/' + PodID_01 + '/acStates?apiKey=' + APIKey + '&limit=1&fields=acState"', 5000)
    } while (!PodStatus.contains('"status": "success"'))
    
    val String PodOn = (transform("JSONPATH", "$.result[0].acState.on", PodStatus))
    val String PodMode = (transform("JSONPATH", "$.result[0].acState.mode", PodStatus))
    val Number PodTarget = new Integer(transform("JSONPATH", "$.result[0].acState.targetTemperature", PodStatus))
    val String PodFan = (transform("JSONPATH", "$.result[0].acState.fanLevel", PodStatus))

    if (PodOn == "true")
      { postUpdate(Sensibo_Living_State, ON) }

    if (PodOn == "false")
      { postUpdate(Sensibo_Living_State, OFF) }

    postUpdate(Sensibo_Living_Target, PodTarget)
    postUpdate(Sensibo_Living_Mode, PodMode)
    postUpdate(Sensibo_Living_Fan, PodFan)

    if (PodStatus.contains('"swing": ')) {
      val String PodSwing = (transform("JSONPATH", "$.result[0].acState.swing", PodStatus))
      postUpdate(Sensibo_Living_Swing, PodSwing)
    }
  }

  catch(Throwable t) {
    logError("Sensibo read state", "Error was caught: {}", t)
  }

end
 rule "Read Sensibo Measurements PodID_01"

  when
    Time cron "0 0/5 * * * ?"
  then

  try {
    var String PodMeasurements
      
    do {
      PodMeasurements = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/pods/' + PodID_01 + '/measurements?apiKey=' + APIKey + '&fields=temperature,humidity,batteryVoltage"', 5000)
      Thread::sleep(5000)
    } while (!PodMeasurements.contains('"status": "success"'))
        
    var PodTemperature = new Double(transform("JSONPATH", "$.result[0].temperature", PodMeasurements))
    var PodHumidity = new Double(transform("JSONPATH", "$.result[0].humidity", PodMeasurements))

    postUpdate(Sensibo_Living_Temp, PodTemperature)
    postUpdate(Sensibo_Living_Humidity, PodHumidity)

    if (!PodMeasurements.contains('"batteryVoltage": null')) {
      val Number PodBattery = new Integer(transform("JSONPATH", "$.result[0].batteryVoltage", PodMeasurements))
      postUpdate(Sensibo_Living_Battery, PodBattery)
    }
  }
  
  catch(Throwable e) {
    logError("Sensibo measurements", "Error was caught: {}", e)
  }

end
rule "Write Sensibo State PodID_01"

  when
    Item Sensibo_Living_State changed or
    Item Sensibo_Living_Mode received command or
    Item Sensibo_Living_Fan received command or
    Item Sensibo_Living_Swing received command or
    Item Sensibo_Living_Target received command
  then
  
  try {

    Thread::sleep(1000)         // Avoid race condition where state has not yet been updated

    var Boolean PodBoolean

    if (Sensibo_Living_State.state == ON)
      { PodBoolean = true }
    else
      { PodBoolean = false }

    var CommandURL = 'https://home.sensibo.com/api/v2/pods/' + PodID_01 + '/acStates?apiKey=' + APIKey

    var CommandState = '{"acState":{"on":' + PodBoolean + ','
    var CommandMode = '"mode":"' + Sensibo_Living_Mode.state + '",'
    var CommandFan = '"fanLevel":"' + Sensibo_Living_Fan.state + '",'
    var CommandSwing = ""

    if (Sensibo_Living_Swing.state != "")
      { CommandSwing = '"swing":"' + Sensibo_Living_Swing.state + '",' }

    var String CommandTemp = '"targetTemperature":' + (Sensibo_Living_Target.state as DecimalType).intValue + '}}'
    var String CommandData = CommandState + CommandMode + CommandFan + CommandSwing + CommandTemp

    var String UpdateResult
    var Attempts = 0
  
    do {
      Attempts += 1
      logInfo("Sensibo command", CommandData)
      UpdateResult = executeCommandLine('curl@@-sSH@@"Content-Type: application/json"@@-XPOST@@' + CommandURL + '@@-d@@'+ CommandData, 10000)
      logInfo("Sensibo result", UpdateResult)
      Thread::sleep(5000)
    } while (!UpdateResult.contains('"status": "success"') && Attempts < 5)
  
    if (Attempts == 5) {
      logError("Sensibo write", "5 failures updating Sensibo")
    }      
  }

  catch(Throwable t) {
    logError("Sensibo write", "Error was caught: {}", t)
  }

end

I’m assuming I just repeat as above for PODID_02, but change the item to Sensibo_Bedroom_*** ?

In regards to error:

400 BadRequest - Sensibo 400 BadRequest POST data is not valid JSON

I’ve put in double backslashes like this:

    var String CommandState = '{\\"acState\\":{\\"on\\":' + PodBoolean + ','
    var String CommandMode = '\\"mode\\":\\"' + SensiboMode.state + '\\",'
    var String CommandFan = '\\"fanLevel\\":\\"' + SensiboFan.state + '\\",'
    var String CommandSwing = ""
	var String CommandTempU = '\\"temperatureUnit\\":\\"C\\"'

    if (SensiboSwing.state != "")
      { CommandSwing = ',\\"swing\\":\\"' + SensiboSwing.state + '\\"}}' }

    var String CommandTemp = '\\"targetTemperature\\":' + (SensiboTarget.state as DecimalType).intValue + ','
    var String CommandData = CommandState + CommandMode + CommandFan + CommandTemp + CommandTempU + CommandSwing

And it works.
I’m on Windows10 x64