So I grabbed a Bond bridge to control my Fanimation fan and it works great for speed, direction and on/off but none of the non-standard commands setup in the Bond app appear in my fan device in Hubitat, i.e. the breeze mode that automatically varies the fan speed to simulate a breeze. Not a deal killer, but it'd be nice to be able to program a Pico button to activate it.
My Leviosa shades have a open next, close next command that is not included in the HE standard shade drive for the BOND hub. I asked in the official thread if it could be added but I never could get them to answer me one way or the other.
I had already added the commands to the user app found here, so for my shades I just keep them on there.
Do you have any examples of how you added the commands? I'm assuming it required editing the drivers a bit? The default Bond integration commands work flawlessly otherwise.
I had to modify the app and the shade driver.
It's been a couple years ago since I did it. My shade cannot do a 0 to 100% setting. You define setpoints and you can raise and lower the shade to the next setpoint. The Bond app has a Open Next and Close Next option but the HE bond app does not. For whatever reason HE did not seem inclined to add it. The changes I made are shown below, should be a similar thing for you.
I have pasted all the code below instead of just the things I added. In case I missed something, as I said this was a couple years ago. You can do a compare of this against the original code listed on GITHUB if you need to.
This is the app code, I added a HandleCloseNext and HandleOpenNext. Didn't really document anything, and it may not be done correctly but it works.
/**
* https://raw.githubusercontent.com/dcmeglio/hubitat-bond/master/apps/BOND_Home_Integration.groovy
*
* BOND Home Integration
*
* Copyright 2019-2020 Dominick Meglio
*
* Revision History
* 2020.01.18 - Added setPosition support for motorized shades, mapping a special value of 50 to the Preset command
* 2019.12.01 - Fixed an issue where dimmers wouldn't work with fans that support direction controls, fixed an issue setting flame height
* 2019.11.24 - Added support for timer based fan light dimmers and flame height adjustment for fireplaces
* 2019.12.14 - Added support for Switch capability to the motorized shades for compatibility
* 2020.01.02 - Fixed an issue where fan speed wouldn't be set properly (thanks jchurch for the troubleshooting!)
* 2020.02.01 - Fixed an issue where looking for devices was incorrect which broke Smart By BOND devices (thanks mcneillk for the fix!)
* 2020.03.23 - Added the ability to fix device state when it's out of sync (thanks stephen_nutt for the suggestion)
* 2020.04.13 - Added a stop command to motorized shades to stop an open/close at the current position (suggested by jchurch)
* 2020.04.21 - Added better logging for connection issues to the hub
* 2020.05.04 - Error logging improvements
* 2020.06.28 - Added toggle command to all devices (suggested by jchurch) and support for having multiple Smart by BOND devices (discovered by jhciotti)
*
*/
definition(
name: "BOND Home Integration",
namespace: "dcm.bond",
author: "Dominick Meglio",
description: "Connects to BOND Home hub",
category: "My Apps",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png",
documentationLink: "https://github.com/dcmeglio/hubitat-bond/blob/master/README.md")
preferences {
page(name: "prefHub", title: "BOND")
page(name: "prefListDevices", title: "BOND")
page(name: "prefPowerSensors", title: "BOND")
}
def prefHub() {
return dynamicPage(name: "prefHub", title: "Connect to BOND", nextPage:"prefListDevices", uninstall:false, install: false) {
section("Hub Information"){
input("hubIp", "text", title: "BOND Hub IP", description: "BOND Hub IP Address", required: true)
input("hubToken", "text", title: "BOND Hub Token", description: "BOND Hub Token", required: true)
input("refreshInterval", "number", title: "Poll BOND Home every N seconds", required: true, defaultValue: 30)
input("debugOutput", "bool", title: "Enable debug logging?", defaultValue: true, displayDuringSetup: false, required: false)
}
displayFooter()
}
}
def prefListDevices() {
if (!getDevices())
{
return dynamicPage(name: "prefListDevices", title: "Connection Error", install: false, uninstall: false) {
section("Error") {
paragraph "Unable to retrieve devices. Please verify your BOND Hub ID and Token"
}
displayFooter()
}
}
else
{
return dynamicPage(name: "prefListDevices", title: "Devices", nextPage: "prefPowerSensors", install: false, uninstall: false) {
section("Devices") {
if (state.fireplaceList.size() > 0)
input(name: "fireplaces", type: "enum", title: "Fireplaces", required:false, multiple:true, options:state.fireplaceList, hideWhenEmpty: true)
if (state.fanList.size() > 0)
input(name: "fans", type: "enum", title: "Fans", required:false, multiple:true, options:state.fanList, hideWhenEmpty: true)
if (state.shadeList.size() > 0)
input(name: "shades", type: "enum", title: "Shades", required:false, multiple:true, options:state.shadeList, hideWhenEmpty: true)
if (state.genericList.size() > 0)
input(name: "genericDevices", type: "enum", title: "Generic Devices", required:false, multiple:true, options:state.genericList, hideWhenEmpty: true)
}
displayFooter()
}
}
}
def prefPowerSensors() {
return dynamicPage(name: "prefPowerSensors", title: "Fireplace Power Meters", install: true, uninstall: true, hideWhenEmpty: true) {
section("Fireplace Power Meters") {
paragraph "For each fireplace device you can associate a power meter to more accurately tell when it is powered on"
if (fireplaces != null) {
for (def i = 0; i < fireplaces.size(); i++) {
input(name: "fireplaceSensor${i}", type: "capability.powerMeter", title: "Sensor for ${state.fireplaceList[fireplaces[i]]}", required: false, submitOnChange: true)
}
for (def i = 0; i < fireplaces.size(); i++) {
if (this.getProperty("fireplaceSensor${i}") != null)
input(name: "fireplaceSensorThreshold${i}", type: "number", title: "Sensor threshold for ${state.fireplaceList[fireplaces[i]]}", required: false)
}
}
}
displayFooter()
}
}
def installed() {
logDebug "Installed with settings: ${settings}"
initialize()
}
def updated() {
logDebug "Updated with settings: ${settings}"
unschedule()
unsubscribe()
initialize()
}
def uninstalled() {
logDebug "Uninstalled app"
for (device in getChildDevices())
{
deleteChildDevice(device.deviceNetworkId)
}
}
def initialize() {
logDebug "initializing"
cleanupChildDevices()
createChildDevices()
subscribeSensorEvents()
def refreshEvery = refreshInterval ?: 30
schedule("0/${refreshEvery} * * * * ? *", updateDevices)
}
def getHubId() {
if (state.hubId)
return state.hubId
def params = [
uri: "http://${hubIp}",
path: "/v2/sys/version",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ]
]
try
{
httpGet(params) { resp ->
if (checkHttpResponse("getHubId", resp))
{
state.hubId = resp.data.bondid
}
}
return state.hubId
}
catch (e)
{
checkHttpResponse("getHubId", e.getResponse())
return null
}
}
def getDevices() {
state.fireplaceList = [:]
state.fireplaceDetails = [:]
state.fireplaceProperties = [:]
state.fanList = [:]
state.fanDetails = [:]
state.fanProperties = [:]
state.shadeList = [:]
state.shadeDetails = [:]
state.shadeProperties = [:]
state.genericList = [:]
state.genericDetails = [:]
state.deviceList = [:]
def params = [
uri: "http://${hubIp}",
path: "/v2/devices",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ]
]
try
{
def result = false
httpGet(params) { resp ->
if (checkHttpResponse("getDevices", resp))
{
for (deviceid in resp.data) {
if (deviceid.key == "_")
continue
getDeviceById(deviceid);
}
result = true
}
}
return result
}
catch (e)
{
checkHttpResponse("getDevices", e.getResponse())
return false
}
}
def getDeviceById(id) {
def params = [
uri: "http://${hubIp}",
path: "/v2/devices/${id.key}",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ]
]
try
{
httpGet(params) { resp ->
if (checkHttpResponse("getDeviceById", resp))
{
if (resp.data.type == "FP")
{
state.fireplaceList[id.key] = resp.data.name
state.fireplaceDetails[id.key] = resp.data.actions
state.fireplaceProperties[id.key] = getDeviceProperties(id)
}
else if (resp.data.type == "CF")
{
state.fanList[id.key] = resp.data.name
state.fanDetails[id.key] = resp.data.actions
state.fanProperties[id.key] = getDeviceProperties(id)
}
else if (resp.data.type == "MS")
{
state.shadeList[id.key] = resp.data.name
state.shadeDetails[id.key] = resp.data.actions
state.shadeProperties[id.key] = getDeviceProperties(id)
}
else if (resp.data.type == "GX")
{
state.genericList[id.key] = resp.data.name
state.genericDetails[id.key] = resp.data.actions
}
}
}
}
catch (e)
{
checkHttpResponse("getDeviceById", e.getResponse())
}
}
def getDeviceProperties(id) {
def params = [
uri: "http://${hubIp}",
path: "/v2/devices/${id.key}/properties",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ]
]
def result = null
try
{
httpGet(params) { resp ->
if (checkHttpResponse("getDeviceProperties", resp))
{
result = resp.data
}
}
}
catch (e)
{
checkHttpResponse("getDeviceProperties", e.getResponse())
}
return result
}
def findChildDevice(deviceId) {
def hubId = getHubId()
def dev = getChildDevice("bond:" + deviceId)
if (dev != null)
return dev
return getChildDevice(hubId + ":bond:" + deviceId)
}
def findComponentDevice(dev, deviceId) {
def hubId = getHubId()
def component = dev.getChildDevice("bond:" + deviceId)
if (component != null)
return component
return component?.getChildDevice(hubId + ":bond:" + deviceId) ?: null
}
def getBondIdFromDevice(device) {
if (device?.deviceNetworkId.startsWith("bond:"))
return device?.deviceNetworkId.split(":")[1]
else
return device?.deviceNetworkId.split(":")[2]
}
def deleteComponentDevice(dev, deviceId) {
def hubId = getHubId()
if (dev.getChildDevice("bond:" + deviceId + ":fan"))
dev.deleteChildDevice("bond:" + deviceId + ":fan")
if (dev.getChildDevice(hubId + ":bond:" + deviceId + ":fan"))
dev.deleteChildDevice(hubId + ":bond:" + deviceId + ":fan")
}
def createChildDevices() {
def hubId = getHubId()
if (fireplaces != null)
{
for (fireplace in fireplaces)
{
def fpDevice = findChildDevice(fireplace)
if (!fpDevice)
{
fpDevice = addChildDevice("bond", "BOND Fireplace", hubId + ":bond:" + fireplace, 1234, ["name": state.fireplaceList[fireplace], isComponent: false])\
}
if (state.fireplaceDetails[fireplace].contains("TurnFpFanOn"))
{
if (!findComponentDevice(fpDevice, fireplace + ":fan"))
fpDevice.addChildDevice("bond", "BOND Fireplace Fan", hubId + ":bond:" + fireplace + ":fan", ["name": state.fireplaceList[fireplace] + " Fan", isComponent: true])
}
if (state.fireplaceDetails[fireplace].contains("TurnLightOn"))
{
if (!findComponentDevice(fpDevice, fireplace + ":light"))
fpDevice.addChildDevice("bond", "BOND Fireplace Light", hubId + ":bond:" + fireplace + ":light", ["name": state.fireplaceList[fireplace] + " Light", isComponent: true])
}
}
}
if (fans != null)
{
for (fan in fans)
{
def fanDevice = findChildDevice(fan)
if (!fanDevice)
{
if (state.fanDetails[fan].contains("SetDirection"))
fanDevice = addChildDevice("bond", "BOND Fan With Direction", hubId + ":bond:" + fan, 1234, ["name": state.fanList[fan], isComponent: false])
else
fanDevice = addChildDevice("bond", "BOND Fan", hubId + ":bond:" + fan, 1234, ["name": state.fanList[fan], isComponent: false])
}
if (state.fanDetails[fan].contains("TurnUpLightOn") && state.fanDetails[fan].contains("TurnDownLightOn"))
{
if (state.fanDetails[fan].contains("SetUpLightBrightness") && state.fanDetails[fan].contains("SetDownLightBrightness"))
{
if (!findComponentDevice(fanDevice, fan + ":uplight"))
fanDevice.addChildDevice("bond", "BOND Fan Dimmable Light", hubId + ":bond:" + fan + ":uplight", ["name": state.fanList[fan] + " Up Light", isComponent: true])
if (!findComponentDevice(fanDevice, fan + ":downlight"))
fanDevice.addChildDevice("bond", "BOND Fan Dimmable Light", hubId + ":bond:" + fan + ":downlight", ["name": state.fanList[fan] + " Down Light", isComponent: true])
}
else if (state.fanDetails[fan].contains("StartUpLightDimmer") && state.fanDetails[fan].contains("StartDownLightDimmer"))
{
if (!findComponentDevice(fanDevice, fan + ":uplight"))
fanDevice.addChildDevice("bond", "BOND Fan Timer Light", hubId + ":bond:" + fan + ":uplight", ["name": state.fanList[fan] + " Up Light", isComponent: true])
if (!findComponentDevice(fanDevice, fan + ":downlight"))
fanDevice.addChildDevice("bond", "BOND Fan Timer Light", hubId + ":bond:" + fan + ":downlight", ["name": state.fanList[fan] + " Down Light", isComponent: true])
}
else
{
if (!findComponentDevice(fanDevice, fan + ":uplight"))
fanDevice.addChildDevice("bond", "BOND Fan Light", hubId + ":bond:" + fan + ":uplight", ["name": state.fanList[fan] + " Up Light", isComponent: true])
if (!findComponentDevice(fanDevice, fan + ":downlight"))
fanDevice.addChildDevice("bond", "BOND Fan Light", hubId + ":bond:" + fan + ":downlight", ["name": state.fanList[fan] + " Down Light", isComponent: true])
}
}
else if (state.fanDetails[fan].contains("TurnLightOn"))
{
if (!findComponentDevice(fanDevice, fan + ":light"))
{
if (state.fanDetails[fan].contains("SetBrightness"))
{
fanDevice.addChildDevice("bond", "BOND Fan Dimmable Light", hubId + ":bond:" + fan + ":light", ["name": state.fanList[fan] + " Light", isComponent: true])
}
else if (state.fanDetails[fan].contains("StartDimmer"))
{
fanDevice.addChildDevice("bond", "BOND Fan Timer Light", hubId + ":bond:" + fan + ":light", ["name": state.fanList[fan] + " Light", isComponent: true])
}
else
fanDevice.addChildDevice("bond", "BOND Fan Light", hubId + ":bond:" + fan + ":light", ["name": state.fanList[fan] + " Light", isComponent: true])
}
}
}
}
if (shades != null)
{
for (shade in shades)
{
def shadeDevice = findChildDevice(shade)
if (!shadeDevice)
{
shadeDevice = addChildDevice("bond", "BOND Motorized Shade", hubId + ":bond:" + shade, 1234, ["name": state.shadeList[shade], isComponent: false])
}
}
}
if (genericDevices != null)
{
for (generic in genericDevices)
{
def genericDevice = findChildDevice(generic)
if (!genericDevice)
{
genericDevice = addChildDevice("bond", "BOND Generic Device", hubId + ":bond:" + generic, 1234, ["name": state.genericList[generic], isComponent: false])
}
}
}
}
def cleanupChildDevices()
{
for (device in getChildDevices())
{
def deviceId = device.deviceNetworkId.replace("bond:","")
if (deviceId.contains(":"))
deviceId = deviceId.split(":")[1]
def deviceFound = false
for (fireplace in fireplaces)
{
if (fireplace == deviceId)
{
deviceFound = true
cleanupFPComponents(device, fireplace)
break
}
}
if (deviceFound == true)
continue
for (fan in fans)
{
if (fan == deviceId)
{
deviceFound = true
cleanupFanComponents(device, fan)
break
}
}
if (deviceFound == true)
continue
for (shade in shades)
{
if (shade == deviceId)
{
deviceFound = true
break
}
}
if (deviceFound == true)
continue
for (generic in genericDevices)
{
if (generic == deviceId)
{
deviceFound = true
break
}
}
if (deviceFound == true)
continue
deleteChildDevice(device.deviceNetworkId)
}
}
def cleanupFPComponents(device, fireplace)
{
if (!state.fireplaceDetails[fireplace].contains("TurnFpFanOn"))
{
deleteComponentDevice(device, fireplace + ":fan")
}
if (!state.fireplaceDetails[fireplace].contains("TurnLightOn"))
{
deleteComponentDevice(device, fireplace + ":light")
}
}
def cleanupFanComponents(device, fan)
{
if (!state.fanDetails[fan].contains("TurnUpLightOn") || !state.fanDetails[fan].contains("TurnDownLightOn"))
{
deleteComponentDevice(device, fan + ":uplight")
deleteComponentDevice(device, fan + ":downlight")
}
if (!state.fanDetails[fan].contains("TurnLightOn") || (state.fanDetails[fan].contains("TurnUpLightOn") && state.fanDetails[fan].contains("TurnDownLightOn")))
{
deleteComponentDevice(device, fan + ":light")
}
}
def subscribeSensorEvents() {
if (fireplaces != null)
{
for (def i = 0; i < fireplaces.size(); i++)
{
def sensorDevice = this.getProperty("fireplaceSensor${i}")
if (sensorDevice != null)
{
logDebug "subscribing to power event for ${sensorDevice}"
subscribe(sensorDevice, "power", powerMeterEventHandler)
}
}
}
}
def powerMeterEventHandler(evt) {
logDebug "Received power meter event ${evt}"
for (def i = 0; i < fireplaces.size(); i++)
{
def sensorDevice = this.getProperty("fireplaceSensor${i}")
if (evt.device.id == sensorDevice.id)
{
def fireplace = fireplaces[i];
def fireplaceDevice = getChildDevice("bond:" + fireplace)
def threshold = 10
def value = "on"
if (evt.integerValue < threshold)
value = "off"
if (value != fireplaceDevice.currentValue("switch"))
{
logDebug "current state ${fireplaceDevice.currentValue("switch")} changing to ${value}"
fireplaceDevice.sendEvent(name: "switch", value: value)
}
if (value == "off")
{
def fanDevice = fireplaceDevice.getChildDevice("bond:" + fireplace + ":fan")
if (fanDevice)
fanDevice.sendEvent(name: "speed", value: "off")
}
break;
}
}
}
def updateDevices() {
for (fan in fans) {
def deviceState = getState(fan)
if (deviceState == null)
continue
def device = findChildDevice(fan)
def deviceLight = findComponentDevice(device, fan + ":light")
def deviceUpLight = findComponentDevice(device, fan + ":uplight")
def deviceDownLight = findComponentDevice(device, fan + ":downlight")
if (deviceState.power > 0)
{
device.sendEvent(name: "switch", value: "on")
device.sendEvent(name: "speed", value: translateBondFanSpeedToHE(fan, state.fanProperties[fan].max_speed ?: 3, deviceState.speed))
}
else
{
device.sendEvent(name: "switch", value: "off")
device.sendEvent(name: "speed", value: "off")
}
if (deviceLight)
{
if (deviceState.brightness != null)
{
if (deviceState.light == 0)
deviceLight.sendEvent(name: "level", value: 0)
else
deviceLight.sendEvent(name: "level", value: deviceState.brightness)
}
else
{
if (deviceState.light > 0)
deviceLight.sendEvent(name: "switch", value: "on")
else
deviceLight.sendEvent(name: "switch", value: "off")
}
}
if (deviceUpLight)
{
if (deviceState.up_light_brightness != null)
{
if (deviceState.up_light == 0)
deviceUpLight.sendEvent(name: "level", value: 0)
else
deviceUpLight.sendEvent(name: "level", value: deviceState.up_light_brightness)
}
else
{
if (deviceState.light > 0 && deviceState.up_light > 0)
deviceUpLight.sendEvent(name: "switch", value: "on")
else
deviceUpLight.sendEvent(name: "switch", value: "off")
}
}
if (deviceDownLight)
{
if (deviceState.down_light_brightness != null)
{
if (deviceState.down_light == 0)
deviceDownLight.sendEvent(name: "level", value: 0)
else
deviceDownLight.sendEvent(name: "level", value: deviceState.down_light_brightness)
}
else
{
if (deviceState.light > 0 && deviceState.down_light > 0)
deviceDownLight.sendEvent(name: "switch", value: "on")
else
deviceDownLight.sendEvent(name: "switch", value: "off")
}
}
if (device.hasAttribute("direction"))
{
if (deviceState.direction == 1)
device.sendEvent(name: "direction", value: "forward")
else if (deviceState.direction == -1)
device.sendEvent(name: "direction", value: "reverse")
}
}
if (fireplaces != null)
{
for (def i = 0; i < fireplaces.size(); i++)
{
def deviceState = getState(fireplaces[i])
if (deviceState == null)
continue
def device = findChildDevice(fireplaces[i])
def deviceFan = findComponentDevice(device, fireplaces[i] + ":fan")
def deviceLight = findComponentDevice(device, fireplaces[i] + ":light")
if (deviceState.flame > 0 && deviceState.power > 0)
{
if (deviceState.flame <= 25)
device.sendEvent(name: "flame", value: "low")
else if (deviceState.flame <= 50)
device.sendEvent(name: "flame", value: "medium")
else
device.sendEvent(name: "flame", value: "high")
}
else
{
device.sendEvent(name: "flame", value: "off")
}
if (deviceState.power > 0)
{
if (this.getProperty("fireplaceSensor${i}") == null)
{
device.sendEvent(name: "switch", value: "on")
}
if (deviceFan)
{
deviceFan.sendEvent(name: "speed", value: translateBondFanSpeedToHE(fireplaces[i], state.fireplaceProperties?.getAt(fireplaces[i])?.max_speed ?: 3, deviceState.fpfan_speed))
}
if (deviceLight)
{
if (deviceState.light == 1)
deviceLight.sendEvent(name: "switch", value: "on")
else
deviceLight.sendEvent(name: "switch", value: "off")
}
}
else
{
if (this.getProperty("fireplaceSensor${i}") == null)
{
device.sendEvent(name: "switch", value: "off")
}
if (deviceFan)
{
deviceFan.sendEvent(name: "speed", value: "off")
}
if (deviceLight)
{
deviceLight.sendEvent(name: "switch", value: "off")
}
}
}
}
if (shades != null)
{
for (shade in shades)
{
def deviceState = getState(shade)
if (deviceState == null)
continue
def device = findChildDevice(shade)
if (deviceState.open == 1)
{
device.sendEvent(name: "switch", value: "on")
device.sendEvent(name: "windowShade", value: "open")
}
else
{
device.sendEvent(name: "switch", value: "off")
device.sendEvent(name: "windowShade", value: "close")
}
}
}
if (genericDevices != null)
{
for (generic in genericDevices)
{
def deviceState = getState(generic)
if (deviceState == null)
continue
def device = findChildDevice(generic)
if (deviceState.power > 0)
{
device.sendEvent(name: "switch", value: "on")
}
else
{
device.sendEvent(name: "switch", value: "off")
}
}
}
}
def handleOn(device) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling On event for ${bondId}"
if (executeAction(bondId, "TurnOn") && shouldSendEvent(bondId))
{
device.sendEvent(name: "switch", value: "on")
}
}
def handleLightOn(device) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Light On event for ${bondId}"
if (device.deviceNetworkId.contains("uplight"))
{
if (executeAction(bondId, "TurnUpLightOn"))
{
device.sendEvent(name: "switch", value: "on")
}
}
else if (device.deviceNetworkId.contains("downlight"))
{
if (executeAction(bondId, "TurnDownLightOn"))
{
device.sendEvent(name: "switch", value: "on")
}
}
else
{
if (executeAction(bondId, "TurnLightOn"))
{
device.sendEvent(name: "switch", value: "on")
}
}
}
def handleLightOff(device) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Light Off event for ${bondId}"
if (device.deviceNetworkId.contains("uplight"))
{
if (executeAction(bondId, "TurnUpLightOff"))
{
device.sendEvent(name: "switch", value: "off")
}
}
else if (device.deviceNetworkId.contains("downlight"))
{
if (executeAction(bondId, "TurnDownLightOff"))
{
device.sendEvent(name: "switch", value: "off")
}
}
else
{
if (executeAction(bondId, "TurnLightOff"))
{
device.sendEvent(name: "switch", value: "off")
}
}
}
def handleDim(device, duration)
{
def bondId = getBondIdFromDevice(device)
if (device.deviceNetworkId.contains("uplight"))
{
dimUsingTimer(device, bondId, duration, "StartUpLightDimmer")
}
else if (device.deviceNetworkId.contains("downlight"))
{
dimUsingTimer(device, bondId, duration, "StartDownLightDimmer")
}
else
{
dimUsingTimer(device, bondId, duration, "StartDimmer")
}
}
def dimUsingTimer(device, duration, command)
{
def bondId = getBondIdFromDevice(device)
if (executeAction(bondId, command))
{
runInMillis((duration*1000).toInteger(), stopDimmer, [data: [device: device, bondId: bondId]])
}
}
def stopDimmer(data)
{
executeAction(data.bondId, "Stop")
}
def handleStartDimming(device)
{
def bondId = getBondIdFromDevice(device)
if (device.deviceNetworkId.contains("uplight"))
{
executeAction(bondId, "StartUpLightDimmer")
}
else if (device.deviceNetworkId.contains("downlight"))
{
executeAction(bondId, "StartDownLightDimmer")
}
else
{
executeAction(bondId, "StartDimmer")
}
}
def handleStopDimming(device)
{
def bondId = getBondIdFromDevice(device)
executeAction(bondId, "Stop")
}
def handleLightLevel(device, level) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Light Level event for ${bondId}"
if (device.deviceNetworkId.contains("uplight"))
{
if (executeAction(bondId, "SetUpLightBrightness", level))
{
device.sendEvent(name: "level", value: level)
}
}
else if (device.deviceNetworkId.contains("downlight"))
{
if (executeAction(bondId, "SetDownLightBrightness", level))
{
device.sendEvent(name: "level", value: level)
}
}
else
{
if (executeAction(bondId, "SetBrightness", level))
{
device.sendEvent(name: "level", value: level)
}
}
}
def handleSetFlame(device, height)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Flame event for ${bondId}"
if (height == "off")
{
if (handleOff(device))
device.sendEvent(name: "flame", value: "off")
}
else
{
def flameHeight = 0
if (height == "low")
flameHeight = 1
else if (height == "medium")
flameHeight = 50
else if (height == "high")
flameHeight = 100
if (executeAction(bondId, "SetFlame", flameHeight))
{
device.sendEvent(name: "flame", value: height)
}
}
}
def handleOpen(device)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Open event for ${bondId}"
if (executeAction(bondId, "Open"))
{
device.sendEvent(name: "windowShade", value: "open")
}
}
def handleOpenNext(device)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Open event for ${bondId}"
if (executeAction(bondId, "OpenNext"))
{
device.sendEvent(name: "windowShade", value: "open")
}
}
def handleClose(device)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Close event for ${bondId}"
if (executeAction(bondId, "Close"))
{
device.sendEvent(name: "windowShade", value: "closed")
}
}
def handleCloseNext(device)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Close event for ${bondId}"
if (executeAction(bondId, "CloseNext"))
{
device.sendEvent(name: "windowShade", value: "closed")
}
}
def handleStop(device)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Stop event for ${bondId}"
executeAction(bondId, "Hold")
}
def handlePreset(device)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Preset event for ${bondId}"
executeAction(bondId, "Preset")
}
def fixPowerState(device, state)
{
def bondId = getBondIdFromDevice(device)
logDebug "Setting power state for ${bondId} to ${state}"
def power
if (state == "on")
power = 1
else
power = 0
if (executeFixState(bondId, '{"power": ' + power + '}'))
{
if (power == 1)
device.sendEvent(name: "switch", value: "on")
else
{
device.sendEvent(name: "switch", value: "off")
if (device.hasAttribute("speed"))
device.sendEvent(name: "speed", value: "off")
if (device.hasAttribute("flame"))
device.sendEvent(name: "flame", value: "off")
}
}
}
def fixFlameState(device, state)
{
def bondId = getBondIdFromDevice(device)
logDebug "Setting flame state for ${bondId} to ${state}"
def flameHeight = 0
if (height == "low")
flameHeight = 1
else if (height == "medium")
flameHeight = 50
else if (height == "high")
flameHeight = 100
if (executeFixState(bondId, '{"flame": ' + flameHeight + '}'))
{
device.sendEvent(name: "flame", value: height)
}
}
def fixFanSpeed(device, fanState)
{
def bondId = getBondIdFromDevice(device)
def speed = translateHEFanSpeedToBond(bondId, state.fanProperties?.getAt(bondId)?.max_speed ?: 3, fanState)
logDebug "Setting fan speed for ${bondId} to ${fanState}"
if (fanState == "off")
{
if (executeFixState(bondId, '{"power": 0}'))
{
device.sendEvent(name: "speed", value: "off")
}
}
else
{
if (executeFixState(bondId, '{"speed": ' + speed + '}'))
{
device.sendEvent(name: "speed", value: fanState)
}
}
}
def fixShadeState(device, state)
{
def bondId = getBondIdFromDevice(device)
logDebug "Setting shade state for ${bondId} to ${state}"
def open
if (state == "open")
open = 1
else
open = 0
if (executeFixState(bondId, '{"open": ' + open + '}'))
{
if (open == 1)
{
device.sendEvent(name: "switch", value: "on")
device.sendEvent(name: "windowShade", value: "open")
}
else
{
device.sendEvent(name: "switch", value: "off")
device.sendEvent(name: "windowShade", value: "closed")
}
}
}
def fixDirection(device, state)
{
def bondId = getBondIdFromDevice(device)
logDebug "Setting direction state for ${bondId} to ${state}"
def direction
if (state == "forward")
direction = 1
else
direction = -1
if (executeFixState(bondId, '{"direction": ' + direction + '}'))
{
if (direction == 1)
{
device.sendEvent(name: "direction", value: "forward")
}
else
{
device.sendEvent(name: "direction", value: "reverse")
}
}
}
def fixFPFanPower(device, state)
{
def bondId = getBondIdFromDevice(device)
logDebug "Setting FP fan power state for ${bondId} to ${state}"
def fppower
if (state == "on")
fppower = 1
else
fppower = 0
if (executeFixState(bondId, '{"fpfan_power": ' + fppower + '}'))
{
if (fppower == 1)
{
device.sendEvent(name: "switch", value: "on")
device.sendEvent(name: "speed", value: "on")
}
else
{
device.sendEvent(name: "switch", value: "off")
device.sendEvent(name: "speed", value: "off")
}
}
}
def fixFPFanSpeed(device, fanState)
{
def bondId = getBondIdFromDevice(device)
logDebug "Setting FP fan speed state for ${bondId} to ${fanState}"
def speed = translateHEFanSpeedToBond(bondId, state.fireplaceProperties?.getAt(bondId)?.max_speed ?: 3, fanState)
if (fanState == "off")
{
if (executeFixState(bondId, '{"fpfan_power": 0}'))
{
device.sendEvent(name: "speed", value: "off")
device.sendEvent(name: "switch", value: "off")
}
}
else
{
if (executeFixState(bondId, '{"fpfan_speed": ' + speed + '}'))
{
device.sendEvent(name: "speed", value: fanState)
device.sendEvent(name: "switch", value: "on")
}
}
}
def fixLightPower(device, state) {
def bondId = getBondIdFromDevice(device)
logDebug "Setting light state for ${bondId} to ${state}"
def power
if (state == "on")
power = 1
else
power = 0
if (device.deviceNetworkId.contains("uplight"))
{
if (executeFixState(bondId, '{"up_light": ' + power + '}'))
{
device.sendEvent(name: "switch", value: state)
}
}
else if (device.deviceNetworkId.contains("downlight"))
{
if (executeFixState(bondId, '{"down_light": ' + power + '}'))
{
device.sendEvent(name: "switch", value: state)
}
}
else
{
if (executeFixState(bondId, '{"light": ' + power + '}'))
{
device.sendEvent(name: "switch", value: state)
}
}
}
def fixLightLevel(device, state) {
def bondId = getBondIdFromDevice(device)
logDebug "Setting light level for ${bondId} to ${state}"
if (device.deviceNetworkId.contains("uplight"))
{
if (executeFixState(bondId, '{"up_light_brightness": ' + state + '}'))
{
device.sendEvent(name: "level", value: state)
}
if (state == 0)
{
if (executeFixState(bondId, '{"up_light": 0}'))
{
device.sendEvent(name: "switch", value: "off")
}
}
}
else if (device.deviceNetworkId.contains("downlight"))
{
if (executeFixState(bondId, '{"down_light_brightness": ' + state + '}'))
{
device.sendEvent(name: "level", value: state)
}
if (state == 0)
{
if (executeFixState(bondId, '{"down_light": 0}'))
{
device.sendEvent(name: "switch", value: "off")
}
}
}
else
{
if (executeFixState(bondId, '{"brightness": ' + state + '}'))
{
device.sendEvent(name: "level", value: state)
}
if (state == 0)
{
if (executeFixState(bondId, '{"light": 0}'))
{
device.sendEvent(name: "switch", value: "off")
}
}
}
}
def translateBondFanSpeedToHE(id, max_speeds, speed)
{
def speedTranslations =
[
10: [10: "high", 9: "high", 8: "medium-high", 7: "medium-high", 6: "medium", 5: "medium", 4: "medium-low", 3: "medium-low", 2: 1, 1: "low"],
9: [9: "high", 8: "medium-high", 7: "medium-high", 6: "medium", 5: "medium", 4: "medium-low", 3: "medium-low", 2: "low", 1: "low"],
8: [8: "high", 7: "medium-high", 6: "medium-high", 5: "medium", 4: "medium", 3: "medium-low", 2: "medium-low", 1: "low"],
7: [7: "high", 6: "medium-high", 5: "medium", 4: "medium", 3: "medium-low", 2: "medium-low", 1: "low"],
6: [6: "high", 5: "medium-high", 4: "medium", 3: "medium", 2: "medium-low", 1: "low"],
5: [5: "high", 4: "medium-high", 3: "medium", 2: "medium-low", 1: "low"],
4: [4: "high", 3: "medium", 2: "medium-low", 1: "low"],
3: [3: "high", 2: "medium", 1: "low"],
2: [2: "high", 1: "low" ]
]
if (!speed.toString().isNumber())
return speed
if (max_speeds > 10 || speed > max_speeds)
return 0
logDebug "${id} -> Translating ${speed}:${max_speeds} to HE ${speedTranslations[max_speeds][speed]}"
return speedTranslations[max_speeds][speed]
}
def translateHEFanSpeedToBond(id, max_speeds, speed)
{
if (speed.isNumber())
return speed.toInteger()
def speedTranslations =
[
10: ["high": 10, "medium-high": 8, "medium": 5, "medium-low": 3, "low": 1],
9: ["high": 9, "medium-high": 7, ":medium": 5, "medium-low": 3, "low": 1],
8: ["high": 8, "medium-high": 6, "medium": 4, "medium-low": 3, "low": 1],
7: ["high": 7, "medium-high": 6, "medium": 4, "medium-low": 3, "low": 1 ],
6: ["high": 6, "medium-high": 5, "medium": 3, "medium-low": 2, "low": 1],
5: ["high": 5, "medium-high": 4, "medium": 3, "medium-low": 2, "low": 1],
4: ["high": 4, "medium": 3, "medium-low": 2, "low": 1],
3: ["high": 3, "medium": 2, "low": 1],
2: ["high": 2, "low": 1]
]
if (max_speeds > 10)
return null
logDebug "${id} -> Translating ${speed}:${max_speeds} to BOND ${speedTranslations[max_speeds][speed]}"
return speedTranslations[max_speeds][speed]
}
def handleFanSpeed(device, speed) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Fan Speed event for ${bondId}"
if (speed == "off")
{
if (handleOff(device))
{
device.sendEvent(name: "speed", value: "off")
}
}
else if (speed == "on")
handleOn(device)
else
{
if (executeAction(bondId, "SetSpeed", translateHEFanSpeedToBond(bondId, state.fanProperties?.getAt(bondId)?.max_speed ?: 3, speed)))
{
device.sendEvent(name: "switch", value: "on")
device.sendEvent(name: "speed", value: speed)
}
}
}
def handleFPFanSpeed(device, speed) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Fireplace Fan Speed event for ${bondId}"
if (speed == "off")
handleFPFanOff(device)
else if (speed == "on")
handleFPFanOn(device)
else
{
if (executeAction(bondId, "SetSpeed", translateHEFanSpeedToBond(bondId, state.fireplaceProperties?.getAt(bondId)?.max_speed ?: 3, speed)))
{
device.sendEvent(name: "speed", value: speed)
}
}
}
def handleFPFanOn(device) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Fan On event for ${bondId}"
if (executeAction(bondId, "TurnFpFanOn"))
{
device.sendEvent(name: "switch", value: "on")
device.sendEvent(name: "speed", value: "on")
return true
}
return false
}
def handleFPFanOff(device) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Fan Off event for ${bondId}"
if (executeAction(bondId, "TurnFpFanOff"))
{
device.sendEvent(name: "switch", value: "off")
device.sendEvent(name: "speed", value: "off")
return true
}
return false
}
def handleOff(device) {
def bondId = getBondIdFromDevice(device)
logDebug "Handling Off event for ${bondId}"
if (executeAction(bondId, "TurnOff") && shouldSendEvent(bondId))
{
device.sendEvent(name: "switch", value: "off")
if (device.hasCapability("FanControl"))
device.sendEvent(name: "speed", value: "off")
return true
}
return false
}
def handleDirection(device, direction)
{
def bondId = getBondIdFromDevice(device)
logDebug "Handling Direction event for ${bondId}"
def bondDirection = 1
if (direction == "reverse")
bondDirection = -1
if (executeAction(bondId, "SetDirection", bondDirection))
{
device.sendEvent(name: "direction", value: direction)
}
}
def getState(bondId) {
def params = [
uri: "http://${hubIp}",
path: "/v2/devices/${bondId}/state",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ]
]
def stateToReturn = null
try
{
httpGet(params) { resp ->
if (checkHttpResponse("getState", resp))
stateToReturn = resp.data
}
}
catch (java.net.NoRouteToHostException e) {
//log.error "getState: connection to BOND hub appears to be down. No Route,Check if the IP is correct."
}
catch (org.apache.http.conn.ConnectTimeoutException e)
{
//log.error "getState: connection to BOND hub appears to be down. Timeout, Check if the IP is correct."
}
catch (Exception e)
{
checkHttpResponse("getState", e.getResponse())
}
return stateToReturn
}
def hasAction(bondId, commandType) {
logDebug "searching for ${commandType} for ${bondId}"
def params = [
uri: "http://${hubIp}",
path: "/v2/devices/${bondId}/actions",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ]
]
def commandToReturn = false
try
{
httpGet(params) { resp ->
if (checkHttpResponse("hasAction", resp))
{
for (commandId in resp.data) {
if (commandId.key == "_")
continue
if (commandId.key == commandType) {
logDebug "found command ${commandId.key} for ${bondId}"
commandToReturn = true
break
}
}
}
}
}
catch (java.net.NoRouteToHostException e) {
log.error "executeFixState: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (org.apache.http.conn.ConnectTimeoutException e)
{
log.error "hasAction: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (Exception e)
{
checkHttpResponse("hasAction", e.getResponse())
}
return commandToReturn
}
def executeAction(bondId, action) {
def params = [
uri: "http://${hubIp}",
path: "/v2/devices/${bondId}/actions/${action}",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ],
body: "{}"
]
def isSuccessful = false
logDebug "${bondId} -> calling action ${action}"
try
{
httpPut(params) { resp ->
isSuccessful = checkHttpResponse("executeAction", resp)
}
}
catch (java.net.NoRouteToHostException e) {
log.error "executeAction: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (org.apache.http.conn.ConnectTimeoutException e)
{
log.error "executeAction: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (Exception e)
{
checkHttpResponse("executeAction", e.getResponse())
}
return isSuccessful
}
def executeAction(bondId, action, argument) {
def params = [
uri: "http://${hubIp}",
path: "/v2/devices/${bondId}/actions/${action}",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ],
body: '{"argument": ' + argument +'}'
]
def isSuccessful = false
logDebug "calling action ${action} ${params.body}"
try
{
httpPut(params) { resp ->
isSuccessful = checkHttpResponse("executeAction", resp)
}
}
catch (java.net.NoRouteToHostException e) {
log.error "executeAction: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (org.apache.http.conn.ConnectTimeoutException e)
{
log.error "executeAction: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (Exception e)
{
checkHttpResponse("executeAction", e.getResponse())
}
return isSuccessful
}
def executeFixState(bondId, body) {
def params = [
uri: "http://${hubIp}",
path: "/v2/devices/${bondId}/state",
contentType: "application/json",
headers: [ 'BOND-Token': hubToken ],
body: body
]
def isSuccessful = false
logDebug "calling fix state ${params.body}"
try
{
httpPatch(params) { resp ->
isSuccessful = checkHttpResponse("executeFixState", resp)
}
}
catch (java.net.NoRouteToHostException e) {
log.error "executeFixState: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (org.apache.http.conn.ConnectTimeoutException e)
{
log.error "executeFixState: connection to BOND hub appears to be down. Check if the IP is correct."
}
catch (Exception e)
{
checkHttpResponse("executeFixState", e.getResponse())
}
return isSuccessful
}
def shouldSendEvent(bondId) {
for (fan in fans)
{
if (fan == bondId)
return true;
}
if (fireplaces != null)
{
for (def i = 0; i < fireplaces.size(); i++)
{
if (fireplaces[i] == bondId)
{
if (this.getProperty("fireplaceSensor${i}") != null)
return false;
return true;
}
}
}
return true;
}
def logDebug(msg) {
if (settings?.debugOutput) {
log.debug msg
}
}
def checkHttpResponse(action, resp) {
if (resp.status == 200 || resp.status == 201 || resp.status == 204)
return true
else if (resp.status == 400 || resp.status == 401 || resp.status == 404 || resp.status == 409 || resp.status == 500)
{
log.error "${action}: ${resp.status} - ${resp.getData()}"
return false
}
else
{
log.error "${action}: unexpected HTTP response: ${resp.status}"
return false
}
}
def displayFooter(){
section() {
paragraph getFormat("line")
paragraph "<div style='color:#1A77C9;text-align:center'>BOND Home Integration<br><a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7LBRPJRLJSDDN&source=url' target='_blank'><img src='https://www.paypalobjects.com/webstatic/mktg/logo/pp_cc_mark_37x23.jpg' border='0' alt='PayPal Logo'></a><br><br>Please consider donating. This app took a lot of work to make.<br>If you find it valuable, I'd certainly appreciate it!</div>"
}
}
def getFormat(type, myText=""){ // Modified from @Stephack Code
if(type == "line") return "<hr style='background-color:#1A77C9; height: 1px; border: 0;'>"
if(type == "title") return "<h2 style='color:#1A77C9;font-weight: bold'>${myText}</h2>"
}
I also had to modify the BOND Motorized Shade driver code, see below. I define two routines raise and lower and added corresponding controls. Raise takes the shade to the next up setpoint and lower takes it to the next down setpoint.
/**
* BOND Motorized Shade
*
* Copyright 2019-2020 Dominick Meglio
*
*/
metadata {
definition (
name: "BOND Motorized Shade",
namespace: "bond",
author: "dmeglio@gmail.com",
importUrl: "https://raw.githubusercontent.com/dcmeglio/hubitat-bond/master/drivers/BOND_Motorized_Shade.groovy"
) {
capability "WindowShade"
capability "Switch"
command "stop"
command "fixShade", [[name:"Shade*", type: "ENUM", description: "Shade", constraints: ["open","close"] ] ]
command "toggle"
command "lower"
command "raise"
}
}
def open() {
parent.handleOpen(device)
}
def raise() {
parent.handleOpenNext(device)
}
def close() {
parent.handleClose(device)
}
def lower() {
parent.handleCloseNext(device)
}
def on() {
open()
}
def off() {
close()
}
def toggle() {
if (device.currentValue("windowShade") == "open")
close()
else
open()
}
def stop() {
parent.handleStop(device)
}
def fixShade(shade) {
parent.fixShadeState(device, shade)
}
def setPosition(Number position) {
if (position == 0) {
log.info "position special value 0 is set, trigger CLose command"
close()
} else if (position == 50) {
log.info "position special value 50 is set, triggering Preset command"
parent.handlePreset(device)
} else if (position == 100) {
log.info "position special value 100 is set, triggering Open command"
open()
} else {
log.info "no-op for position value " + position + ", set position to 50 to trigger Preset command"
}
}
I miss Dominick Meglio. He contributed so much to the Hubitat platform and made it a better place.