-
-
Save joshualyon/c0ee0834ad0890bf267812d55cfd8be5 to your computer and use it in GitHub Desktop.
Shimmed Switch capability into Bond Window Shade for Hubitat community driver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* https://raw.githubusercontent.com/dcmeglio/hubitat-bond/master/apps/BOND_Home_Integration.groovy | |
* | |
* BOND Home Integration | |
* | |
* Copyright 2019 Dominick Meglio | |
* | |
* Revision History | |
* v 2019.12.01 - Fixed an issue where dimmers wouldn't work with fans that support direction controls, fixed an issue setting flame height | |
* v 2019.11.24 - Added support for timer based fan light dimmers and flame height adjustment for fireplaces | |
* | |
*/ | |
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") | |
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") | |
input("hubToken", "text", title: "BOND Hub Token", description: "BOND Hub Token") | |
input("debugOutput", "bool", title: "Enable debug logging?", defaultValue: true, displayDuringSetup: false, required: false) | |
} | |
} | |
} | |
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" | |
} | |
} | |
} | |
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) | |
} | |
} | |
} | |
} | |
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) | |
} | |
} | |
} | |
} | |
} | |
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() | |
schedule("0/30 * * * * ? *", updateDevices) | |
} | |
def getDevices() { | |
state.fireplaceList = [:] | |
state.fireplaceDetails = [:] | |
state.fireplaceProperties = [:] | |
state.fanList = [:] | |
state.fanDetails = [:] | |
state.fanProperties = [:] | |
state.shadeList = [:] | |
state.shadeDetails = [:] | |
state.shadeProperties = [:] | |
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}", | |
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) | |
} | |
} | |
} | |
} | |
catch (e) | |
{ | |
checkHttpResponse("getDeviceById", e.getResponse()) | |
} | |
} | |
def getDeviceProperties(id) { | |
def params = [ | |
uri: "http://${hubIp}", | |
path: "/v2/devices/${id}/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 createChildDevices() { | |
if (fireplaces != null) | |
{ | |
for (fireplace in fireplaces) | |
{ | |
def fpDevice = getChildDevice("bond:" + fireplace) | |
if (!fpDevice) | |
{ | |
fpDevice = addChildDevice("bond", "BOND Fireplace", "bond:" + fireplace, 1234, ["name": state.fireplaceList[fireplace], isComponent: false])\ | |
} | |
if (state.fireplaceDetails[fireplace].contains("TurnFpFanOn")) | |
{ | |
if (!fpDevice.getChildDevice("bond:" + fireplace + ":fan")) | |
fpDevice.addChildDevice("bond", "BOND Fireplace Fan", "bond:" + fireplace + ":fan", ["name": state.fireplaceList[fireplace] + " Fan", isComponent: true]) | |
} | |
if (state.fireplaceDetails[fireplace].contains("TurnLightOn")) | |
{ | |
if (!fpDevice.getChildDevice("bond:" + fireplace + ":light")) | |
fpDevice.addChildDevice("bond", "BOND Fireplace Light", "bond:" + fireplace + ":light", ["name": state.fireplaceList[fireplace] + " Light", isComponent: true]) | |
} | |
} | |
} | |
if (fans != null) | |
{ | |
for (fan in fans) | |
{ | |
def fanDevice = getChildDevice("bond:" + fan) | |
if (!fanDevice) | |
{ | |
if (state.fanDetails[fan].contains("SetDirection")) | |
fanDevice = addChildDevice("bond", "BOND Fan With Direction", "bond:" + fan, 1234, ["name": state.fanList[fan], isComponent: false]) | |
else | |
fanDevice = addChildDevice("bond", "BOND Fan", "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 (!fanDevice.getChildDevice("bond:" + fan + ":uplight")) | |
fanDevice.addChildDevice("bond", "BOND Fan Dimmable Light", "bond:" + fan + ":uplight", ["name": state.fanList[fan] + " Up Light", isComponent: true]) | |
if (!fanDevice.getChildDevice("bond:" + fan + ":downlight")) | |
fanDevice.addChildDevice("bond", "BOND Fan Dimmable Light", "bond:" + fan + ":downlight", ["name": state.fanList[fan] + " Down Light", isComponent: true]) | |
} | |
else if (state.fanDetails[fan].contains("StartUpLightDimmer") && state.fanDetails[fan].contains("StartDownLightDimmer")) | |
{ | |
if (!fanDevice.getChildDevice("bond:" + fan + ":uplight")) | |
fanDevice.addChildDevice("bond", "BOND Fan Timer Light", "bond:" + fan + ":uplight", ["name": state.fanList[fan] + " Up Light", isComponent: true]) | |
if (!fanDevice.getChildDevice("bond:" + fan + ":downlight")) | |
fanDevice.addChildDevice("bond", "BOND Fan Timer Light", "bond:" + fan + ":downlight", ["name": state.fanList[fan] + " Down Light", isComponent: true]) | |
} | |
else | |
{ | |
if (!fanDevice.getChildDevice("bond:" + fan + ":uplight")) | |
fanDevice.addChildDevice("bond", "BOND Fan Light", "bond:" + fan + ":uplight", ["name": state.fanList[fan] + " Up Light", isComponent: true]) | |
if (!fanDevice.getChildDevice("bond:" + fan + ":downlight")) | |
fanDevice.addChildDevice("bond", "BOND Fan Light", "bond:" + fan + ":downlight", ["name": state.fanList[fan] + " Down Light", isComponent: true]) | |
} | |
} | |
else if (state.fanDetails[fan].contains("TurnLightOn")) | |
{ | |
if (!fanDevice.getChildDevice("bond:" + fan + ":light")) | |
{ | |
if (state.fanDetails[fan].contains("SetBrightness")) | |
{ | |
fanDevice.addChildDevice("bond", "BOND Fan Dimmable Light", "bond:" + fan + ":light", ["name": state.fanList[fan] + " Light", isComponent: true]) | |
} | |
else if (state.fanDetails[fan].contains("StartDimmer")) | |
{ | |
fanDevice.addChildDevice("bond", "BOND Fan Timer Light", "bond:" + fan + ":light", ["name": state.fanList[fan] + " Light", isComponent: true]) | |
} | |
else | |
fanDevice.addChildDevice("bond", "BOND Fan Light", "bond:" + fan + ":light", ["name": state.fanList[fan] + " Light", isComponent: true]) | |
} | |
} | |
} | |
} | |
if (shades != null) | |
{ | |
for (shade in shades) | |
{ | |
def shadeDevice = getChildDevice("bond:" + shade) | |
if (!shadeDevice) | |
{ | |
shadeDevice = addChildDevice("bond", "BOND Motorized Shade", "bond:" + shade, 1234, ["name": state.shadeList[shade], isComponent: false]) | |
} | |
} | |
} | |
} | |
def cleanupChildDevices() | |
{ | |
for (device in getChildDevices()) | |
{ | |
def deviceId = device.deviceNetworkId.replace("bond:","") | |
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 | |
deleteChildDevice(device.deviceNetworkId) | |
} | |
} | |
def cleanupFPComponents(device, fireplace) | |
{ | |
if (!state.fireplaceDetails[fireplace].contains("TurnFpFanOn")) | |
{ | |
device.deleteChildDevice("bond:" + fireplace + ":fan") | |
} | |
if (!state.fireplaceDetails[fireplace].contains("TurnLightOn")) | |
{ | |
device.deleteChildDevice("bond:" + fireplace + ":light") | |
} | |
} | |
def cleanupFanComponents(device, fan) | |
{ | |
if (!state.fanDetails[fan].contains("TurnUpLightOn") || !state.fanDetails[fan].contains("TurnDownLightOn")) | |
{ | |
device.deleteChildDevice("bond:" + fan + ":uplight") | |
device.deleteChildDevice("bond:" + fan + ":downlight") | |
} | |
if (!state.fanDetails[fan].contains("TurnLightOn") || (state.fanDetails[fan].contains("TurnUpLightOn") && state.fanDetails[fan].contains("TurnDownLightOn"))) | |
{ | |
device.deleteChildDevice("bond:" + 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 state = getState(fan) | |
def device = getChildDevice("bond:" + fan) | |
def deviceLight = device.getChildDevice("bond:" + fan + ":light") | |
def deviceUpLight = device.getChildDevice("bond:" + fan + ":uplight") | |
def deviceDownLight = device.getChildDevice("bond:" + fan + ":downlight") | |
if (state.power > 0) | |
{ | |
device.sendEvent(name: "switch", value: "on") | |
device.sendEvent(name: "speed", value: translateBondFanSpeedToHE(state.fanProperties?.getAt(fan)?.max_speed ?: 3, state.speed)) | |
} | |
else | |
{ | |
device.sendEvent(name: "switch", value: "off") | |
device.sendEvent(name: "speed", value: "off") | |
} | |
if (deviceLight) | |
{ | |
if (state.brightness != null) | |
{ | |
if (state.light == 0) | |
deviceLight.sendEvent(name: "level", value: 0) | |
else | |
deviceLight.sendEvent(name: "level", value: state.brightness) | |
} | |
else | |
{ | |
if (state.light > 0) | |
deviceLight.sendEvent(name: "switch", value: "on") | |
else | |
deviceLight.sendEvent(name: "switch", value: "off") | |
} | |
} | |
if (deviceUpLight) | |
{ | |
if (state.up_light_brightness != null) | |
{ | |
if (state.up_light == 0) | |
deviceUpLight.sendEvent(name: "level", value: 0) | |
else | |
deviceUpLight.sendEvent(name: "level", value: state.up_light_brightness) | |
} | |
else | |
{ | |
if (state.light > 0 && state.up_light > 0) | |
deviceUpLight.sendEvent(name: "switch", value: "on") | |
else | |
deviceUpLight.sendEvent(name: "switch", value: "off") | |
} | |
} | |
if (deviceDownLight) | |
{ | |
if (state.down_light_brightness != null) | |
{ | |
if (state.down_light == 0) | |
deviceDownLight.sendEvent(name: "level", value: 0) | |
else | |
deviceDownLight.sendEvent(name: "level", value: state.down_light_brightness) | |
} | |
else | |
{ | |
if (state.light > 0 && state.down_light > 0) | |
deviceDownLight.sendEvent(name: "switch", value: "on") | |
else | |
deviceDownLight.sendEvent(name: "switch", value: "off") | |
} | |
} | |
if (device.hasAttribute("direction")) | |
{ | |
if (state.direction == 1) | |
device.sendEvent(name: "direction", value: "forward") | |
else if (state.direction == -1) | |
device.sendEvent(name: "direction", value: "reverse") | |
} | |
} | |
if (fireplaces != null) | |
{ | |
for (def i = 0; i < fireplaces.size(); i++) | |
{ | |
def state = getState(fireplaces[i]) | |
def device = getChildDevice("bond:" + fireplaces[i]) | |
def deviceFan = device.getChildDevice("bond:" + fireplaces[i] + ":fan") | |
def deviceLight = device.getChildDevice("bond:" + fireplaces[i] + ":light") | |
if (state.flame > 0 && state.power > 0) | |
{ | |
if (state.flame <= 25) | |
device.sendEvent(name: "flame", value: "low") | |
else if (state.flame <= 50) | |
device.sendEvent(name: "flame", value: "medium") | |
else | |
device.sendEvent(name: "flame", value: "high") | |
} | |
else | |
{ | |
device.sendEvent(name: "flame", value: "off") | |
} | |
if (state.power > 0) | |
{ | |
if (this.getProperty("fireplaceSensor${i}") == null) | |
{ | |
device.sendEvent(name: "switch", value: "on") | |
} | |
if (deviceFan) | |
{ | |
deviceFan.sendEvent(name: "speed", value: translateBondFanSpeedToHE(state.fireplaceProperties?.getAt(fireplaces[i])?.max_speed ?: 3, state.fpfan_speed)) | |
} | |
if (deviceLight) | |
{ | |
if (state.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 state = getState(shade) | |
def device = getChildDevice("bond:" + shade) | |
if (state.open == 1) | |
{ | |
device.sendEvent(name: "windowShade", value: "open") | |
device.sendEvent(name: "switch", value: "on") //map the shade=open -> switch=on as a compatibility shim | |
} | |
else | |
{ | |
device.sendEvent(name: "windowShade", value: "closed") | |
device.sendEvent(name: "switch", value: "off") //map the shade=closed -> switch=off as a compatibility shim | |
} | |
} | |
} | |
} | |
def handleOn(device, bondId) { | |
logDebug "Handling On event for ${bondId}" | |
if (executeAction(bondId, "TurnOn") && shouldSendEvent(bondId)) | |
{ | |
device.sendEvent(name: "switch", value: "on") | |
} | |
} | |
def handleLightOn(device, bondId) { | |
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, bondId) { | |
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, bondId, duration) | |
{ | |
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, bondId, duration, command) | |
{ | |
if (executeAction(bondId, command)) | |
{ | |
runInMillis((duration*1000).toInteger(), stopDimmer, [data: [device: device, bondId: bondId]]) | |
} | |
} | |
def stopDimmer(data) | |
{ | |
executeAction(data.bondId, "Stop") | |
} | |
def handleStartDimming(device, bondId) | |
{ | |
if (device.deviceNetworkId.contains("uplight")) | |
{ | |
executeAction(bondId, "StartUpLightDimmer") | |
} | |
else if (device.deviceNetworkId.contains("downlight")) | |
{ | |
executeAction(bondId, "StartDownLightDimmer") | |
} | |
else | |
{ | |
executeAction(bondId, "StartDimmer") | |
} | |
} | |
def handleStopDimming(device, bondId) | |
{ | |
executeAction(bondId, "Stop") | |
} | |
def handleLightLevel(device, bondId, level) { | |
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, bondId, height) | |
{ | |
logDebug "Handling Flame event for ${bondId}" | |
if (height == "off") | |
{ | |
if (handleOff(device, bondId)) | |
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, bondId) | |
{ | |
logDebug "Handling Open event for ${bondId}" | |
if (executeAction(bondId, "Open")) | |
{ | |
device.sendEvent(name: "windowShade", value: "open") | |
device.sendEvent(name: "switch", value: "on") //map the shade=open -> switch=on as a compatibility shim | |
} | |
} | |
def handleClose(device, bondId) | |
{ | |
logDebug "Handling Close event for ${bondId}" | |
if (executeAction(bondId, "Close")) | |
{ | |
device.sendEvent(name: "windowShade", value: "closed") | |
device.sendEvent(name: "switch", value: "off") //map the shade=closed -> switch=off as a compatibility shim | |
} | |
} | |
def translateBondFanSpeedToHE(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" ] | |
] | |
logDebug "Translating ${speed} to BOND" | |
if (!speed.toString().isNumber()) | |
return speed | |
if (max_speeds > 10 || speed > max_speeds) | |
return 0 | |
return speedTranslations[max_speeds][speed] | |
} | |
def translateHEFanSpeedToBond(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 | |
return speedTranslations[max_speeds][speed] | |
} | |
def handleFanSpeed(device, bondId, speed) { | |
logDebug "Handling Fan Speed event for ${bondId}" | |
if (speed == "off") | |
{ | |
if (handleOff(device, bondId)) | |
device.sendEvent(name: "speed", value: "off") | |
} | |
else if (speed == "on") | |
handleOn(device, bondId) | |
else | |
{ | |
if (executeAction(bondId, "SetSpeed", translateHEFanSpeedToBond(state.fanProperties?.getAt(bondId)?.max_speed ?: 3, speed))) | |
{ | |
device.sendEvent(name: "switch", value: "on") | |
device.sendEvent(name: "speed", value: speed) | |
} | |
} | |
} | |
def handleFPFanSpeed(device, bondId, speed) { | |
logDebug "Handling Fireplace Fan Speed event for ${bondId}" | |
if (speed == "off") | |
handleFPFanOff(device, bondId) | |
else if (speed == "on") | |
handleFPFanOn(device, bondId) | |
else | |
{ | |
if (executeAction(bondId, "SetSpeed", translateHEFanSpeedToBond(state.fireplaceProperties?.getAt(bondId)?.max_speed ?: 3, speed))) | |
{ | |
device.sendEvent(name: "speed", value: speed) | |
} | |
} | |
} | |
def handleFPFanOn(device, bondId) { | |
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, bondId) { | |
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, bondId) { | |
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, bondId, direction) | |
{ | |
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 (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 (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 "calling action ${action}" | |
try | |
{ | |
httpPut(params) { resp -> | |
isSuccessful = checkHttpResponse("executeAction", resp) | |
} | |
} | |
catch (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 (e) | |
{ | |
checkHttpResponse("executeAction", 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.getData()}" | |
return false | |
} | |
else | |
{ | |
log.error "${action}: unexpected HTTP response: ${resp.status}" | |
return false | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* BOND Motorized Shade | |
* | |
* Copyright 2019 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" //as a fallback for when the Window Shade capability isn't supported, but the Switch is | |
} | |
} | |
def open() { | |
parent.handleOpen(device, device.deviceNetworkId.split(":")[1]) | |
} | |
def close() { | |
parent.handleClose(device, device.deviceNetworkId.split(":")[1]) | |
} | |
//map the switch commands to the window shade commands | |
def on(){ open(); } | |
def off(){ close(); } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment