My A/C supports the following swing modes:
- stopped
- fixedTop
- fixedMiddleTop
- fixedMiddle
- fixedMiddleBottom
- fixedBottom
- rangeFull
My A/C supports the following swing modes:
I will add those options later so everyone can use their A/C to tue fullest and then start a rework with the bridge solution. Also the binding is now available on the eclipse marketplace
I also notified Sensibo about the development so they can maybe update their smart home integration website.
UPDATE: I changed the swing mode from switch to string with the options provided. Note that some A/C may not be compatible with all of them (your A/C won’t react to the change then, I checked it). The fix should be available under the already provided URL after the build finished (around 1,5 to 2 hours).
I’ll be concentrating on redesigning the binding to use a bridge to determine the available options, but I won’t be able to do that before the weekend and it may take some time, so please be patient.
Thanks for the binding I can now control my AC.
How do I read the status of and initialize the different channels?
Current_temperature and Humidity works but the other channels are not initialized unless I change them from PaperUI or BasicUI.
If I use https://home.sensibo.com/api/v2/pods/{device_id}?fields=acState&apiKey={api_key} status is returned correct.
I use OpenHABian on a Pi and when I start OpenHAB I have the following in my log:
Yes currently only the temperature and humidity is being synchronized. I’ll add the rest as soon as I can solve my current problem. I’ll check the exception then too.
The binding is awesome work however I not using currently as wife uses old remote and I use the google home sometimes.
I have 2 Sensibo Sky units working currently in my rules. These update all states to openhab periodically. So you can send Sensibo the command that the state the aircon is in. Sensibo ignores this so to me that’s ok.
Rules
/*** openHAB Sensibo Rules ***/
// Goto the website https://home.sensibo.com/me/api first and create APIKey
val String APIKey = "<Enter API key>"
var PodID_01 = "<enter your pod id here>" //Living Room
var PodID_02 = "<enter your pod id here>" //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
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
Sitemap Example
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"
}
}
I hard coded the api and pod_id because you only do it once.
However @Zodiarc Looks like he is doing a binding that will fill in that data for you. I am new to this and am quickly learning how to do stuff. Also @Zodiarc “I’ll add the rest as soon as I can solve my current problem” maybe it is something we can help you with. I probably won’t be able to help but if I know what the problem is I or others may be able to help. If it all comes together in binding I won’t have to rewrite all of my rules to use PATCH instead of posting all of the commands again to Sensibo
Keep up the good work. I have a solution running and am not trying to hurry you at all.
I would just like to thank you for all the effort you are putting into openHAB I read the other tread and it looks like you are nearly there. It looks like others are having the same issues as you are and their is a gap in the documentation. I see that you got the swing chanel to work so you are nearly there.
You may have probably seen the yamaha binding it may be another example of running code that works.
The problem is not the channel type but the channel options.
Development of the binding has been canceled. Sorry to inform you about it but I won´t develop this binding any further. I made no progress for over a month since dynamic state descriptors simply don´t work although my implementation has been confirmed for correct and now the build is even failing die to errors in files I didn´t touch for weeks. I also don´t have the time anymore to work on it further. The code will remain up on my github, so if anyone wants to take over, feel free to do so.
Thank you for your efforts.
Is anyone interested in continuing the development of this binding?
As I can see the only “big” thing which needs to be added is to fetch all data from the device (not just temp and humidity).
Anyway is there someone who uses the older rules to control Sensibo? Is it still working? Which is better right now?
New implementation submitted, prebuilt binary and documentation found by reading https://github.com/openhab/openhab2-addons/pull/5576
Testing feedback welcome.
Arne
Thank you very much! I will test it this weekend!
Can it do multiple devices I will test now
I can’t install it on 2.4 stable, GSON import is missing:
Error while starting bundle: file:/usr/share/openhab2/addons/org.openhab.binding.sensibo-2.5.0-SNAPSHOT.jar
org.osgi.framework.BundleException: Could not resolve module: org.openhab.binding.sensibo [284]
Unresolved requirement: Import-Package: com.google.gson; version="[2.8.0,3.0.0)"
at org.eclipse.osgi.container.Module.start(Module.java:444) ~[?:?]
at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:383) ~[?:?]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundle(DirectoryWatcher.java:1260) [10:org.apache.felix.fileinstall:3.6.4]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundles(DirectoryWatcher.java:1233) [10:org.apache.felix.fileinstall:3.6.4]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.doProcess(DirectoryWatcher.java:520) [10:org.apache.felix.fileinstall:3.6.4]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.process(DirectoryWatcher.java:365) [10:org.apache.felix.fileinstall:3.6.4]
at org.apache.felix.fileinstall.internal.DirectoryWatcher.run(DirectoryWatcher.java:316) [10:org.apache.felix.fileinstall:3.6.4]
Yes, 2.5.0+ builds have gotten this dependency upgrade requirement.
You can install the newer gson bundle from maven repo; jar (235 KB)
Just copy it into the addons folder like you did with the binding file.
Hmm, I have added it, but the same error happens. Maybe a restart is required…
I will test this further on the weekend
Yes it works after restart
Thank You for getting this going excellent work.
I running openHAB 2.4.0-1 (Release Build) openhabian on RPI3
It is working good for both my sky’s
The only thing is the names come up as the mac addresses like
sensibo:sensibosky:home:45cfd940ee11:targetTemperature
Things File
Bridge sensibo:account:home "Sensibo account" [apiKey="Awesom_Work", refreshInterval=120] {
Thing sensibosky lounge "Sensibo Sky Lounge" [ macAddress="45:cf:d9:40:ee:11" ]
Thing sensibosky bedroom "Sensibo Sky Bedroom" [ macAddress="45:cf:58:0e:bd:d6" ]
}
The naming of the thing I thought would have been
sensibo:sensibosky:home:lounge
sensibo:sensibosky:home:bedroom
Like the hue:0210:mybridge:bulb1 example in
Thankyou again I can delete redundant rules now.
For others following along.
Easy way to find mac address is to navigate to
https://home.sensibo.com/api/v2/users/me/pods?fields=*&apiKey=YOURKEY
and find “macAddress”: “42:cb:d9:04:re:11”,
Changed things file and they are coming up as expected now.
Bridge sensibo:account:home "Sensibo account" [apiKey="ENTER_KEY_HERE", refreshInterval=120] {
Thing sensibosky lounge "Sensibo Sky Lounge" @ "Lounge" [ macAddress="" ]
Thing sensibosky bedroom "Sensibo Sky Bedroom" @ "Bedroom" [ macAddress="" ]
}