Sensibo API Integration - Based on Rules

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

Are there here anyone planning to make a binding for this one?

I still have that error message on macOS. First of all I had to change the following line:

UpdateResult = executeCommandLine('curl@@-sSH@@"Content-Type: application/json"@@-XPOST@@' + CommandURL + '@@-d@@'+ CommandData, 10000)`

To (included additional code for proper understanding):

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 CommandTempUnit = '"temperatureUnit":"C",'
var String CommandData = CommandState + CommandMode + CommandFan + CommandTemp + CommandTempUnit + CommandSwing

var String UpdateResult
var Number Attempts = 0

var CommandExec = "curl -sSH \"Accept: application/json\" -H \"Content-Type: application/json\" -X POST -d \'" + CommandData + "\' " + CommandURL

...

UpdateResult = executeCommandLine(CommandExec, 10000)

As before it was throwing different error messages like URL is incorrect, syntax is incorrect etc. For some reason macOS doesn’t like @@. After replacing @@ with just spaces and slightly changing the structure of the curl request it started sending requests to Sensibo. But it returns ‘400 BadRequest - Sensibo 400 BadRequest POST data is not valid JSON’. At the same time if I execute the same command from the command line it works perfectly

curl -sSH "Accept: application/json" -H "Content-Type: application/json" -X POST -d '{"acState":{"on":true,"mode":"cool","fanLevel":"auto","targetTemperature":24,"temperatureUnit":"C","swing":"stopped"}}' https://home.sensibo.com/api/v2/pods/<PodID>/acStates?apiKey=<APIKey>

PodID and APIKey have been removed for obvious reasons, but that line executed from CLI works perfectly. It turns on A/C and returns proper response from Sensibo. While this command executed in openHAB it just gives ‘POST data is not valid JSON’. Don’t understand why… Anything I’m missing?

Tried to use

logInfo("Sensibo Exec:", CommandExec)

And it gives the perfect CLI line, but still doesn’t work from the code.

Any help would be highly appreciated! I’m really new to openHAB and Java…

Did you end up getting it working I having the same result

<html><head><title>400 BadRequest - Sensibo</title></head><body><h>400 BadRequest</h><p>POST data is not valid JSON</p><body></html>

Yes, I managed to fix it. It was the issue with syntax. Here is the correct line that works for me,

var CommandExec = 'curl@@-sSH@@"Accept: application/json"@@-H@@"Content-Type: application/json"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL

Let me know if you want the entire file.

Thanks that works great

So with the help I got here I got it all working. My goal is to have controls on my HASP plate (touch screen that replaces wall switched).
I needed the pod state to mainly be stored in the sensibo api so I changed the code a little. Instead of posting changes I will just post the files It updates every 5 min but I will change it to update when I change to that page on the touchscreen.

sitemap sensibo label="Sensibo" {
  Frame label="Sensibo Living" {
    Switch    item=Sensibo_Living_State      label="Sensibo_Living_State" mappings=["true"="ON", "false"="OFF"] icon="switch"                              
    Switch    item=Sensibo_Living_Mode       label="Mode"                     icon="heating"        mappings=[cool="Cool",heat="Heat",fan="Fan"]
    Setpoint  item=Sensibo_Living_Target     label="Target [%.0f ºC]"         icon="temperature"    minValue=18 maxValue=26 step=1.0
    Switch    item=Sensibo_Living_Fan        label="Fan"                      icon="fan"            mappings=[low="Low",medium="Med",high="High",auto="Auto"]
    Switch    item=Sensibo_Living_Swing      label="Swing"                    icon="fan"            mappings=[stopped="Stop",fixedMiddle="Middle",fixedTop="High",rangeFull="Swing"]
    Text      item=Sensibo_Living_Temp       label="Temperature [%.1f ºC]"    icon="temperature"
    Text      item=Sensibo_Living_Humidity   label="Humidity [%.0f %%]"       icon="water"
   // Text      item=Sensibo_Living_Battery    label="Battery [%d mV]"          icon="energy"
  }
  
  Frame label="Sensibo Bedroom" {
    Switch    item=Sensibo_Bedroom_State      label="Sensibo Bedroom State"     mappings=["true"="ON", "false"="OFF"] icon="switch"
    Switch    item=Sensibo_Bedroom_Mode       label="Mode"                     icon="heating"        mappings=[cool="Cool",heat="Heat",fan="Fan"]
    Setpoint  item=Sensibo_Bedroom_Target     label="Target [%.0f ºC]"         icon="temperature"    minValue=18 maxValue=26 step=1.0
    Switch    item=Sensibo_Bedroom_Fan        label="Fan"                      icon="fan"            mappings=[low="Low",medium="Med",high="High",auto="Auto"]
    Switch    item=Sensibo_Bedroom_Swing      label="Swing"                    icon="fan"            mappings=[stopped="Stop",fixedMiddle="Middle",fixedTop="High",rangeFull="Swing"]
    Text      item=Sensibo_Bedroom_Temp       label="Temperature [%.1f ºC]"    icon="temperature"
    Text      item=Sensibo_Bedroom_Humidity   label="Humidity [%.0f %%]"       icon="water"
   // Text      item=Sensibo_Bedroom_Battery    label="Battery [%d mV]"          icon="energy"
  }
}

Items

String Sensibo_Living_State  
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


String Sensibo_Bedroom_State
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

Rules
You need to enter your own apikey and podid

/*** openHAB Sensibo Rules ***/

// Goto the website https://home.sensibo.com/me/api first and create APIKey

val String APIKey = ""


var PodID_01 = ""  //Living 
var PodID_02 = ""  //Bedroom


rule "Read Sensibo State  PodID_01"

  when
    System started or
    Time cron "0 0/5 * * * ?" //Update states from senibo every 5 min
  then

  //Thread::sleep(1000)

  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)
      //logInfo("Sensibo result - PodStatus", PodStatus)
    } 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))

    postUpdate(Sensibo_Living_State, PodOn)
    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 * * * ?"  //Update states from senibo every 5 min
  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)
      //logInfo("Sensibo result - PodMeasurements", PodMeasurements)
      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 received command 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 CommandURL = 'https://home.sensibo.com/api/v2/pods/' + PodID_01 + '/acStates?apiKey=' + APIKey

    var CommandState = '{"acState":{"on":' + Sensibo_Living_State.state + ','
    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 CommandTempUnit = '"temperatureUnit":"C",'
    var String CommandData = CommandState + CommandMode + CommandFan + CommandTemp + CommandTempUnit + CommandSwing

    var String UpdateResult
    var Number Attempts = 0

    var CommandExec = 'curl@@-sSH@@"Accept: application/json"@@-H@@"Content-Type: application/json"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL
  
    do {
      Attempts += 1
      //logInfo("Sensibo command", CommandData)
      UpdateResult = executeCommandLine(CommandExec, 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

/***  Bedroom Sensibo ***/

rule "Read Sensibo State  PodID_02"

  when
    System started or
    Time cron "0 0/5 * * * ?"  //Update states from senibo every 5 min
  then

  //Thread::sleep(1000)

  try {
    var String PodStatus 
      
    do {
      Thread::sleep(5000)
      PodStatus = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/pods/' + PodID_02 + '/acStates?apiKey=' + APIKey + '&limit=1&fields=acState"', 5000)
      //logInfo("Sensibo result - PodStatus", PodStatus)
    } 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))

    postUpdate(Sensibo_Bedroom_State, PodOn)
    postUpdate(Sensibo_Bedroom_Target, PodTarget)
    postUpdate(Sensibo_Bedroom_Mode, PodMode)
    postUpdate(Sensibo_Bedroom_Fan, PodFan)

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

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

end

rule "Read Sensibo Measurements PodID_02"

  when
    Time cron "0 0/5 * * * ?"  //Update states from senibo every 5 min
  then

  try {
    var String PodMeasurements
      
    do {
      PodMeasurements = executeCommandLine('curl -sSH "Accept: application/json"     "https://home.sensibo.com/api/v2/pods/' + PodID_02 + '/measurements?apiKey=' + APIKey + '&fields=temperature,humidity,batteryVoltage"', 5000)
      //logInfo("Sensibo result - PodMeasurements", PodMeasurements)
      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_Bedroom_Temp, PodTemperature)
    postUpdate(Sensibo_Bedroom_Humidity, PodHumidity)

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

end

rule "Write Sensibo State PodID_02"

  when
    Item Sensibo_Bedroom_State received command or
    Item Sensibo_Bedroom_Mode received command or
    Item Sensibo_Bedroom_Fan received command or
    Item Sensibo_Bedroom_Swing received command or
    Item Sensibo_Bedroom_Target received command
  then
  
  try {

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

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

    var CommandState = '{"acState":{"on":' + Sensibo_Bedroom_State.state + ','
    var CommandMode = '"mode":"' + Sensibo_Bedroom_Mode.state + '",'
    var CommandFan = '"fanLevel":"' + Sensibo_Bedroom_Fan.state + '",'
    var CommandSwing = ""

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

    var String CommandTemp = '"targetTemperature":' + (Sensibo_Bedroom_Target.state as DecimalType).intValue + ','
    var String CommandTempUnit = '"temperatureUnit":"C",'
    var String CommandData = CommandState + CommandMode + CommandFan + CommandTemp + CommandTempUnit + CommandSwing

    var String UpdateResult
    var Number Attempts = 0

    var CommandExec = 'curl@@-sSH@@"Accept: application/json"@@-H@@"Content-Type: application/json"@@-X@@POST@@-d@@' + CommandData + '@@' + CommandURL
  
    do {
      Attempts += 1
      //logInfo("Sensibo command", CommandData)
      UpdateResult = executeCommandLine(CommandExec, 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 hope this helps someone and this wan’t possible without the help from the other users above.

1 Like

For anyone starting from scratch, and wanting a short cut to getting this working:

  • Copy the files in to the correct folders (follow the file extension)
  • Remove the battery lines in .items and .sitemap if you have a Sensibo Sky
  • Make sure you have the JSONPath Transformation installed (Paper UI > Configuration > Bindings > Add (+) > Transformations
  • Follow the documentation and add an API key (I called mine “apiKey”), then add the key to the top of the .rules file
  • restart OpenHab (many ways to do this, I do sudo systemctl stop openhab2.service, then sudo systemctl start openhab2.service in console

Then it works beautifully. THANK YOU for the integration.

1 Like

their is a binding now you can use

Had the same problem but managed to fix it in a more “elegant” way by replacing all the executeCommandLine with sendHttpGetRequest and sendHttpPostRequest methods. I guess those methods are more efficient than execute a curl command using a shell.

PodMeasurements = sendHttpGetRequest(“https://home.sensibo.com/api/v2/pods/” + PodID + “/measurements?apiKey=” + APIKey + “&fields=temperature,humidity,batteryVoltage”)

And for the update

UpdateResult = sendHttpPostRequest(CommandURL,“application/json”, CommandData)

Hello gents,

I noticed that since 2019-07-14 12:10:00 the Sensibo API call doesn’t work anymore. The Sensibo API was changed.

[ERROR] [rthome.model.script.Pod measurements] - Error reading from Sensibo API

The new call should be per:

 sendHttpGetRequest("https://home.sensibo.com/api/v2/pods/" + PodID + "?fields=measurements&apiKey=" + APIKey + "")

This gives below new JSON formatted output

[arthome.model.script.Pod Measurement] - {"status": "success", "result": {"measurements": {"batteryVoltage": null, "temperature": 22.5, "humidity": 66.6, "time": {"secondsAgo": 540, "time": "2019-07-21T08:16:00Z"}, "rssi": "-64", "piezo": [null, null]}}}

I’m busy changing the API output interpretation but run into a “NumberFormatException” error.

      val Number PodTemperature = new Double(transform("JSONPATH", "$.result[0].measurements.temperature", PodMeasurements))
      val Number PodHumidity = new Double(transform("JSONPATH", "$.result[0].measurements.humidity", PodMeasurements))
java.lang.NumberFormatException: For input string: "{"status": "success", "result": {"measurements": {"batteryVoltage": null, "temperature": null, "humidity": null, "time": null, "rssi": null, "piezo": [null, null]}}}"
	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043) ~[?:?]
	at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110) ~[?:?]
	at java.lang.Double.parseDouble(Double.java:538) ~[?:?]
	at java.lang.Double.<init>(Double.java:608) ~[?:?]
	at sun.reflect.GeneratedConstructorAccessor333.newInstance(Unknown Source) ~[?:?]

Help on this last step is appreciated after which I will donate once again my rules script for this sensibo integration.

Regards,
Ferry

Is this still under development? I have some old v1 pods laying around that I’d love to use again. While looking for a OH3 solution I stumbled on this discussion. I’m a OH beginner, so I need to be taken by hand to make this work.
There are Sensibo bindings, but they are for the v2 Sky.
Anybody willing to port this to OH3?
Thanks!

That’s great! Sharing your Sensibo control files on GitHub is a helpful resource for others who want to control their Sensibo systems in a similar way. This could also be useful for developers who are interested in building applications that interact with Sensibo using a business information API. By making your code publicly available, you’re contributing to the developer community and potentially inspiring others to create innovative new tools.