Compare commits

...

23 Commits

Author SHA1 Message Date
Bob Florian
64f671ceb8 Merge pull request #2267 from bflorian/CDH-8-lifx-cirrus
CDH-8 Add Cirrus support to LIFX
2017-08-28 21:47:32 -07:00
Jack Chi
57f179b770 Merge pull request #2258 from skt123/aeon_key_fob
[DVCSMP-2841] Onboard Aeotec Panic Button
2017-08-28 10:21:21 -07:00
Bob Florian
f78faa036e CDH-8 Add Cirrus support to LIFX 2017-08-28 09:22:15 -07:00
Aaron Miller
f9ea07ab81 Merge pull request #1929 from aaron-miller/DVCSMP-2591
[DVCSMP-2591] Switch IFTTT SmartApp to use async client
2017-08-24 14:47:24 -05:00
sushant.k1
a9e0918ed7 [DVCSMP-2841]
Onboard Aeotec Panic Button
2017-08-23 20:30:42 +05:30
Vinay Rao
f4367fa2f1 Merge pull request #2255 from SmartThingsCommunity/staging
Rolling down staging to master
2017-08-22 16:47:08 -07:00
Ingvar Marstorp
40cfb980cf DVCSMP-2973 CT100/Honeywell Z-wave NullPointerException (#2251)
* DVCSMP-2973 CT100/Honeywell Z-wave NullPointerException
CT100/Honeywell Z-wave NullPointerException when changing mode
In the event supportedModes and supportedFanModes hasn't been updated from a string to a list the mode isn't validated properly.
Adding check for string and if so updating the supported modes.

* Update ct100-thermostat.groovy

* Update zwave-thermostat.groovy
2017-08-22 12:24:14 -07:00
Vinay Rao
c7151b235a Merge pull request #2244 from SmartThingsCommunity/master
Rolling up master to staging
2017-08-15 18:00:58 -07:00
Vinay Rao
51bf0a6df2 Merge pull request #2243 from SmartThingsCommunity/staging
Rolling down staging to master
2017-08-15 17:57:19 -07:00
Aaron Miller
1312526a09 Merge pull request #2203 from aaron-miller/DVCSMP-2889
[DVCSMP-2889] Reduce Log Messages in Ecobee Connect SA
2017-08-15 09:38:52 -05:00
Aaron Miller
969b4b3a31 Merge pull request #2205 from aaron-miller/DVCSMP-2890
[DVCSMP-2890] Reduce LIFX Connect Logging
2017-08-15 09:38:38 -05:00
Ryan Applegate
c7bd232046 Merge pull request #1983 from rappleg/AddJitterSmartLightTimer
[DVCSMP-2673] Add jitter to Smart Light Timer X Minutes schedule
2017-08-15 09:25:06 -05:00
Jack Chi
b0e28ba968 Merge pull request #2228 from skt123/fibaro_smoke_sensor_v1
[DVCSMP-2957] Adding CRC16 encapsulation for Fibaro Smoke Sensor
2017-08-11 17:21:15 -07:00
sushant.k1
1611b21aa6 [DVCSMP-2957]
Adding CRC16 encapsulation Fibaro Smoke Sensor.
2017-08-12 03:54:26 +05:30
dsainteclaire
27d9bdd86c Merge pull request #2231 from marstorp/dvcsmp2973
DVCSMP-2973 CT100/Honeywell Z-wave NullPointerException
2017-08-10 16:52:35 -07:00
Ingvar Marstorp
2b09b7c574 Update zwave-thermostat.groovy 2017-08-10 12:23:50 -07:00
Ingvar Marstorp
eb9b55e01c Update ct100-thermostat.groovy 2017-08-10 12:19:12 -07:00
marstorp
10fef276db DVCSMP-2973 CT100/Honeywell Z-wave NullPointerException
CT100/Honeywell Z-wave NullPointerException when changing mode
Changed name of private method setThermostatMode(data) to setGetThermostatMode(data)
and method setThermostatFanMode(data) to setGetThermostatFanMode(data) so that platform
calls the exposed methods setThermostatMode(String value) setThermostatFanMode(String value)
as these validates the argument against the supported modes and then calls the private methods.
Also removed some of the leftshift operators that showed errors in Sumo logs.
2017-08-10 11:42:05 -07:00
Vinay Rao
2ea99a1b9e Merge pull request #2226 from SmartThingsCommunity/master
Rolling up master to staging
2017-08-08 13:48:18 -07:00
Aaron Miller
4370a552d5 [DVCSMP-2591] Switch IFTTT SmartApp to use async client 2017-08-01 09:45:41 -05:00
Aaron Miller
2cd00cdd51 [DVCSMP-2890] Reduce LIFX Connect Logging 2017-07-31 15:27:53 -05:00
Aaron Miller
485204c461 [DVCSMP-2889] Reduce Log Messages in Ecobee Connect SA 2017-07-31 14:26:06 -05:00
rappleg
66f356d275 [DVCSMP-2673] Add jitter to Smart Light Timer X Minutes schedule 2017-05-23 09:34:02 -05:00
10 changed files with 249 additions and 121 deletions

View File

@@ -38,29 +38,25 @@ metadata {
status "wakeup": "command: 8407, payload: "
}
tiles {
standardTile("button", "device.button", width: 2, height: 2) {
state "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
state "button 1 pushed", label: "pushed #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
state "button 2 pushed", label: "pushed #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
state "button 3 pushed", label: "pushed #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
state "button 4 pushed", label: "pushed #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#00A0DC"
state "button 1 held", label: "held #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
state "button 2 held", label: "held #2", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
state "button 3 held", label: "held #3", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
state "button 4 held", label: "held #4", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#e86d13"
multiAttributeTile(name: "rich-control") {
tileAttribute("device.button", key: "PRIMARY_CONTROL") {
attributeState "default", label: ' ', action: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
}
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat") {
standardTile("battery", "device.battery", inactiveLabel: false, width: 6, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
main "button"
details(["button", "battery"])
childDeviceTiles("outlets")
}
}
def parse(String description) {
def results = []
if (description.startsWith("Err")) {
results = createEvent(descriptionText:description, displayed:true)
results = createEvent(descriptionText:description, displayed:true)
} else {
def cmd = zwave.parse(description, [0x2B: 1, 0x80: 1, 0x84: 1])
if(cmd) results += zwaveEvent(cmd)
@@ -84,9 +80,16 @@ def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
def buttonEvent(button, held) {
button = button as Integer
String childDni = "${device.deviceNetworkId}/${button}"
def child = childDevices.find{it.deviceNetworkId == childDni}
if (!child) {
log.error "Child device $childDni not found"
}
if (held) {
child?.sendEvent(name: "button", value: "held", data: [buttonNumber: 1], descriptionText: "$child.displayName was held", isStateChange: true)
createEvent(name: "button", value: "held", data: [buttonNumber: button], descriptionText: "$device.displayName button $button was held", isStateChange: true)
} else {
child?.sendEvent(name: "button", value: "pushed", data: [buttonNumber: 1], descriptionText: "$child.displayName was pushed", isStateChange: true)
createEvent(name: "button", value: "pushed", data: [buttonNumber: button], descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
}
}
@@ -130,6 +133,17 @@ def installed() {
def updated() {
initialize()
if (!childDevices) {
createChildDevices()
}
else if (device.label != state.oldLabel) {
childDevices.each {
def segs = it.deviceNetworkId.split("/")
def newLabel = "${device.displayName} button ${segs[-1]}"
it.setLabel(newLabel)
}
state.oldLabel = device.label
}
}
def initialize() {
@@ -143,5 +157,15 @@ def initialize() {
if (zwMap && zwMap.mfr == "0086" && zwMap.prod == "0001" && zwMap.model == "0026") {
buttons = 1
}
createChildDevices(buttons)
sendEvent(name: "numberOfButtons", value: buttons)
}
private void createChildDevices(def num) {
state.oldLabel = device.label
for (i in num) {
addChildDevice("Child Button", "${device.deviceNetworkId}/${i}", null,
[completedSetup: true, label: "${device.displayName} button ${i}",
isComponent: true, componentName: "button$i", componentLabel: "Button $i"])
}
}

View File

@@ -18,6 +18,7 @@ metadata {
command "raiseHeatingSetpoint"
command "lowerCoolSetpoint"
command "raiseCoolSetpoint"
command "poll"
fingerprint deviceId: "0x08", inClusters: "0x43,0x40,0x44,0x31,0x80,0x85,0x60"
fingerprint mfr:"0098", prod:"6401", model:"0107", deviceJoinName: "2Gig CT100 Programmable Thermostat"
@@ -101,9 +102,8 @@ metadata {
def installed() {
// Configure device
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format())
cmds << new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()),
new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())]
sendHubCommand(cmds)
runIn(3, "initialize", [overwrite: true]) // Allow configure command to be sent and acknowledged before proceeding
}
@@ -123,7 +123,7 @@ def initialize() {
// Device-Watch simply pings if no device events received for 32min(checkInterval)
sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
// Poll device for additional data that will be updated by refresh tile
poll()
pollDevice()
}
def parse(String description)
@@ -150,7 +150,6 @@ def parse(String description)
def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiInstanceCmdEncap cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 3])
log.debug ("multiinstancev1.MultiInstanceCmdEncap: command from instance ${cmd.instance}: ${encapsulatedCommand}")
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
}
@@ -341,7 +340,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSuppo
if(cmd.auxiliaryemergencyHeat) { supportedModes << "emergency heat" }
state.supportedModes = supportedModes
sendEvent(name: "supportedThermostatModes", value: supportedModes, isStateChange: true, displayed: false)
sendEvent(name: "supportedThermostatModes", value: supportedModes, displayed: false)
}
def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) {
@@ -351,7 +350,7 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod
if(cmd.circulation) { supportedFanModes << "circulate" }
state.supportedFanModes = supportedFanModes
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, isStateChange: true, displayed: false)
sendEvent(name: "supportedThermostatFanModes", value: supportedFanModes, displayed: false)
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
@@ -377,7 +376,6 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "ManufacturerSpecificReport ${cmd}: value:${cmd}"
if (cmd.manufacturerName) {
updateDataValue("manufacturer", cmd.manufacturerName)
}
@@ -389,6 +387,11 @@ log.debug "ManufacturerSpecificReport ${cmd}: value:${cmd}"
}
}
def poll() {
// Call refresh which will cap the polling to once every 2 minutes
refresh()
}
def refresh() {
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
def timeNow = now()
@@ -397,11 +400,11 @@ def refresh() {
// refresh will request battery, prevent multiple request by setting lastbatt now
state.lastbatt = timeNow
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
runIn(2, "poll", [overwrite: true])
runIn(2, "pollDevice", [overwrite: true])
}
}
def poll() {
def pollDevice() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
@@ -614,17 +617,18 @@ def updateThermostatSetpoint(setpoint, value) {
* */
def ping() {
log.debug "ping() called"
// Just get Operating State as it is not reported when it chnages and there's no need to flood more commands
// Just get Operating State as it is not reported when it changes and there's no need to flood more commands
sendHubCommand(new physicalgraph.device.HubAction(zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format()))
}
def switchMode() {
def currentMode = device.currentValue("thermostatMode")
def supportedModes = state.supportedModes
if (supportedModes) {
// Old version of supportedModes was as string, make sure it gets updated
if (supportedModes && supportedModes.size() && supportedModes[0].size() > 1) {
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedModes not defined"
getSupportedModes()
@@ -633,9 +637,10 @@ def switchMode() {
def switchToMode(nextMode) {
def supportedModes = state.supportedModes
if (supportedModes) {
// Old version of supportedModes was as string, make sure it gets updated
if (supportedModes && supportedModes.size() && supportedModes[0].size() > 1) {
if (supportedModes.contains(nextMode)) {
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
}
@@ -654,10 +659,11 @@ def getSupportedModes() {
def switchFanMode() {
def currentMode = device.currentValue("thermostatFanMode")
def supportedFanModes = state.supportedFanModes
if (supportedFanModes) {
// Old version of supportedFanModes was as string, make sure it gets updated
if (supportedFanModes && supportedFanModes.size() && supportedFanModes[0].size() > 1) {
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedFanModes not defined"
getSupportedFanModes()
@@ -666,9 +672,10 @@ def switchFanMode() {
def switchToFanMode(nextMode) {
def supportedFanModes = state.supportedFanModes
if (supportedFanModes) {
// Old version of supportedFanModes was as string, make sure it gets updated
if (supportedFanModes && supportedFanModes.size() && supportedFanModes[0].size() > 1) {
if (supportedFanModes.contains(nextMode)) {
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
}
@@ -679,8 +686,7 @@ def switchToFanMode(nextMode) {
}
def getSupportedFanModes() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())]
sendHubCommand(cmds)
}
@@ -696,10 +702,9 @@ def setThermostatMode(String value) {
switchToMode(value)
}
def setThermostatMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
def setGetThermostatMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())]
sendHubCommand(cmds)
}
@@ -713,10 +718,9 @@ def setThermostatFanMode(String value) {
switchToFanMode(value)
}
def setThermostatFanMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
def setGetThermostatFanMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())]
sendHubCommand(cmds)
}

View File

@@ -78,15 +78,21 @@ metadata {
attributeState("replacement required", label:"REPLACE", icon:"st.alarm.smoke.test", backgroundColor:"#FFFF66")
attributeState("unknown", label:"UNKNOWN", icon:"st.alarm.smoke.test", backgroundColor:"#ffffff")
}
tileAttribute ("device.battery", key: "SECONDARY_CONTROL") {
attributeState "battery", label:'Battery: ${currentValue}%', unit:"%"
}
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:"%"
}
valueTile("temperature", "device.temperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "temperature", label:'${currentValue}°', unit:"C"
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label: '${currentValue}°',
backgroundColors: [
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
valueTile("heatAlarm", "device.heatAlarm", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "clear", label:'TEMPERATURE OK', backgroundColor:"#ffffff"
@@ -100,7 +106,7 @@ metadata {
}
main "smoke"
details(["smoke","temperature"])
details(["smoke","temperature","battery"])
}
}
@@ -163,6 +169,19 @@ def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationRejec
createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
}
//crc16
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd) {
def versions = [0x31: 5, 0x71: 3, 0x84: 1]
def version = versions[cmd.commandClass as Integer]
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
if (!encapsulatedCommand) {
log.debug "Could not extract command from $cmd"
} else {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
setSecured()
def encapsulatedCommand = cmd.encapsulatedCommand([0x31: 5, 0x71: 3, 0x84: 1])
@@ -342,6 +361,8 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
def configure() {
// Device wakes up every 4 hours, this interval allows us to miss one wakeup notification before marking offline
sendEvent(name: "checkInterval", value: 8 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
//making the default state as "clear"
sendEvent(name: "smoke", value: "clear", displayed: false)
// This sensor joins as a secure device if you tripple-click the button to include it
log.debug "configure() >> isSecured() : ${isSecured()}"
if (!isSecured()) {

View File

@@ -5,7 +5,7 @@
*
*/
metadata {
definition (name: "LIFX Color Bulb", namespace: "smartthings", author: "LIFX", ocfDeviceType: "oic.d.light") {
definition (name: "LIFX Color Bulb", namespace: "smartthings", author: "LIFX", ocfDeviceType: "oic.d.light", cloudDeviceHandler: "smartthings.cdh.handlers.LifxLightHandler") {
capability "Actuator"
capability "Color Control"
capability "Color Temperature"

View File

@@ -5,7 +5,7 @@
*
*/
metadata {
definition (name: "LIFX White Bulb", namespace: "smartthings", author: "LIFX", ocfDeviceType: "oic.d.light") {
definition (name: "LIFX White Bulb", namespace: "smartthings", author: "LIFX", ocfDeviceType: "oic.d.light", cloudDeviceHandler: "smartthings.cdh.handlers.LifxLightHandler") {
capability "Actuator"
capability "Color Temperature"
capability "Switch"

View File

@@ -28,6 +28,7 @@ metadata {
command "raiseHeatingSetpoint"
command "lowerCoolSetpoint"
command "raiseCoolSetpoint"
command "poll"
fingerprint deviceId: "0x08"
fingerprint inClusters: "0x43,0x40,0x44,0x31"
@@ -91,7 +92,7 @@ metadata {
standardTile("raiseCoolSetpoint", "device.heatingSetpoint", width:2, height:1, inactiveLabel: false, decoration: "flat") {
state "heatingSetpoint", action:"raiseCoolSetpoint", icon:"st.thermostat.thermostat-right"
}
valueTile("thermostatOperatingState", "device.thermostatOperatingState", width: 2, height:1, decoration: "flat") {
standardTile("thermostatOperatingState", "device.thermostatOperatingState", width: 2, height:1, decoration: "flat") {
state "thermostatOperatingState", label:'${currentValue}', backgroundColor:"#ffffff"
}
standardTile("refresh", "device.thermostatMode", width:2, height:1, inactiveLabel: false, decoration: "flat") {
@@ -105,9 +106,8 @@ metadata {
def installed() {
// Configure device
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format())
cmds << new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format()),
new physicalgraph.device.HubAction(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format())]
sendHubCommand(cmds)
runIn(3, "initialize", [overwrite: true]) // Allow configure command to be sent and acknowledged before proceeding
}
@@ -129,7 +129,7 @@ def initialize() {
if (getDataValue("manufacturer") != "Honeywell") {
runEvery5Minutes("poll") // This is not necessary for Honeywell Z-wave, but could be for other Z-wave thermostats
}
poll()
pollDevice()
}
def parse(String description)
@@ -319,17 +319,22 @@ def zwaveEvent(physicalgraph.zwave.Command cmd) {
}
// Command Implementations
def poll() {
// Call refresh which will cap the polling to once every 2 minutes
refresh()
}
def refresh() {
// Only allow refresh every 2 minutes to prevent flooding the Zwave network
def timeNow = now()
if (!state.refreshTriggeredAt || (2 * 60 * 1000 < (timeNow - state.refreshTriggeredAt))) {
state.refreshTriggeredAt = timeNow
// use runIn with overwrite to prevent multiple DTH instances run before state.refreshTriggeredAt has been saved
runIn(2, "poll", [overwrite: true])
runIn(2, "pollDevice", [overwrite: true])
}
}
def poll() {
def pollDevice() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSupportedGet().format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
@@ -502,10 +507,11 @@ def ping() {
def switchMode() {
def currentMode = device.currentValue("thermostatMode")
def supportedModes = state.supportedModes
if (supportedModes) {
// Old version of supportedModes was as string, make sure it gets updated
if (supportedModes && supportedModes.size() && supportedModes[0].size() > 1) {
def next = { supportedModes[supportedModes.indexOf(it) + 1] ?: supportedModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedModes not defined"
getSupportedModes()
@@ -514,9 +520,10 @@ def switchMode() {
def switchToMode(nextMode) {
def supportedModes = state.supportedModes
if (supportedModes) {
// Old version of supportedModes was as string, make sure it gets updated
if (supportedModes && supportedModes.size() && supportedModes[0].size() > 1) {
if (supportedModes.contains(nextMode)) {
runIn(2, "setThermostatMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("ThermostatMode $nextMode is not supported by ${device.displayName}")
}
@@ -535,10 +542,11 @@ def getSupportedModes() {
def switchFanMode() {
def currentMode = device.currentValue("thermostatFanMode")
def supportedFanModes = state.supportedFanModes
if (supportedFanModes) {
// Old version of supportedFanModes was as string, make sure it gets updated
if (supportedFanModes && supportedFanModes.size() && supportedFanModes[0].size() > 1) {
def next = { supportedFanModes[supportedFanModes.indexOf(it) + 1] ?: supportedFanModes[0] }
def nextMode = next(currentMode)
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.warn "supportedFanModes not defined"
getSupportedFanModes()
@@ -547,9 +555,10 @@ def switchFanMode() {
def switchToFanMode(nextMode) {
def supportedFanModes = state.supportedFanModes
if (supportedFanModes) {
// Old version of supportedFanModes was as string, make sure it gets updated
if (supportedFanModes && supportedFanModes.size() && supportedFanModes[0].size() > 1) {
if (supportedFanModes.contains(nextMode)) {
runIn(2, "setThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
runIn(2, "setGetThermostatFanMode", [data: [nextMode: nextMode], overwrite: true])
} else {
log.debug("FanMode $nextMode is not supported by ${device.displayName}")
}
@@ -560,8 +569,7 @@ def switchToFanMode(nextMode) {
}
def getSupportedFanModes() {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format())]
sendHubCommand(cmds)
}
@@ -577,10 +585,9 @@ def setThermostatMode(String value) {
switchToMode(value)
}
def setThermostatMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())
def setGetThermostatMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeSet(mode: modeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatModeV2.thermostatModeGet().format())]
sendHubCommand(cmds)
}
@@ -594,10 +601,9 @@ def setThermostatFanMode(String value) {
switchToFanMode(value)
}
def setThermostatFanMode(data) {
def cmds = []
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format())
cmds << new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())
def setGetThermostatFanMode(data) {
def cmds = [new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeSet(fanMode: fanModeMap[data.nextMode]).format()),
new physicalgraph.device.HubAction(zwave.thermostatFanModeV3.thermostatFanModeGet().format())]
sendHubCommand(cmds)
}
@@ -663,7 +669,7 @@ def getTempInDeviceScale(temp, scale) {
if (temp && scale) {
def deviceScale = (state.scale == 1) ? "F" : "C"
return (deviceScale == scale) ? temp :
(deviceScale == "F" ? celsiusToFahrenheit(temp) : roundC(fahrenheitToCelsius(temp)))
(deviceScale == "F" ? celsiusToFahrenheit(temp).toDouble().round(0).toInteger() : roundC(fahrenheitToCelsius(temp)))
}
return 0
}

View File

@@ -7,7 +7,7 @@
* If the switch is already on, if won't be affected by the timer (Must be turned of manually)
* If the switch is toggled while in timeout-mode, it will remain on and ignore the timer (Must be turned of manually)
*
* The timeout perid begins when the contact is closed, or motion stops, so leaving a door open won't start the timer until it's closed.
* The timeout period begins when the contact is closed, or motion stops, so leaving a door open won't start the timer until it's closed.
*
* Author: andersheie@gmail.com
* Date: 2014-08-31
@@ -37,31 +37,29 @@ preferences {
}
}
def installed() {
initialize()
}
def installed()
{
def updated() {
unsubscribe()
initialize()
log.debug "state: " + state.myState
}
def initialize() {
subscribe(switches, "switch", switchChange)
subscribe(motions, "motion", motionHandler)
subscribe(contacts, "contact", contactHandler)
schedule("0 * * * * ?", "scheduleCheck")
state.myState = "ready"
}
def updated()
{
unsubscribe()
subscribe(motions, "motion", motionHandler)
subscribe(switches, "switch", switchChange)
subscribe(contacts, "contact", contactHandler)
state.myState = "ready"
log.debug "state: " + state.myState
runEvery1Minute("scheduleCheck")
state.myState = "ready"
}
def switchChange(evt) {
log.debug "SwitchChange: $evt.name: $evt.value"
if(evt.value == "on") {
// Slight change of Race condition between motion or contact turning the switch on,
// versus user turning the switch on. Since we can't pass event parameters :-(, we rely
@@ -84,7 +82,7 @@ def switchChange(evt) {
def contactHandler(evt) {
log.debug "contactHandler: $evt.name: $evt.value"
if (evt.value == "open") {
if(state.myState == "ready") {
log.debug "Turning on lights by contact opening"
@@ -124,7 +122,7 @@ def setActiveAndSchedule() {
unschedule()
state.myState = "active"
state.inactiveAt = now()
schedule("0 * * * * ?", "scheduleCheck")
runEvery1Minute("scheduleCheck")
}
def scheduleCheck() {

View File

@@ -279,7 +279,7 @@ def getEcobeeThermostats() {
Map sensorsDiscovered() {
def map = [:]
log.info "list ${atomicState.remoteSensors}"
log.debug "list ${atomicState.remoteSensors}"
atomicState.remoteSensors.each { sensors ->
sensors.each {
if (it.type != "thermostat") {
@@ -440,7 +440,7 @@ def pollChild() {
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")) {
if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
def tData = atomicState.thermostats[child.device.deviceNetworkId]
log.info "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}"
log.debug "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}"
child.generateEvent(tData.data) //parse received message from parent
} else if(atomicState.thermostats[child.device.deviceNetworkId] == null) {
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId}"
@@ -449,7 +449,7 @@ def pollChild() {
}
}
} else {
log.info "ERROR: pollChildren()"
log.error "ERROR: pollChildren()"
return null
}

View File

@@ -31,6 +31,8 @@
* ---------------------+----------------+--------------------------+------------------------------------
*/
include 'asynchttp_v1'
definition(
name: "IFTTT",
namespace: "smartthings",
@@ -249,9 +251,7 @@ def deviceHandler(evt) {
def deviceInfo = state[evt.deviceId]
if (deviceInfo) {
try {
httpPostJson(uri: deviceInfo.callbackUrl, path: '', body: [evt: [deviceId: evt.deviceId, name: evt.name, value: evt.value]]) {
log.debug "[PROD IFTTT] Event data successfully posted"
}
asynchttp_v1.post([uri: deviceInfo.callbackUrl, path: '', body: [evt: [deviceId: evt.deviceId, name: evt.name, value: evt.value]]])
} catch (groovyx.net.http.ResponseParseException e) {
log.debug("Error parsing ifttt payload ${e}")
}

View File

@@ -5,6 +5,7 @@
*
*/
include 'localization'
include 'cirrus'
definition(
name: "LIFX (Connect)",
@@ -36,7 +37,7 @@ preferences {
mappings {
path("/receivedToken") { action: [ POST: "oauthReceivedToken", GET: "oauthReceivedToken"] }
path("/receiveToken") { action: [ POST: "oauthReceiveToken", GET: "oauthReceiveToken"] }
path("/hookCallback") { action: [ POST: "hookEventHandler", GET: "hookEventHandler"] }
path("/webhookCallback") { action: [ POST: "webhookCallback"] }
path("/oauth/callback") { action: [ GET: "oauthCallback" ] }
path("/oauth/initialize") { action: [ GET: "oauthInit"] }
path("/test") { action: [ GET: "oauthSuccess" ] }
@@ -87,7 +88,7 @@ def authPage() {
def oauthInit() {
def oauthParams = [client_id: "${appSettings.clientId}", scope: "remote_control:all", response_type: "code" ]
log.info("Redirecting user to OAuth setup")
log.debug("Redirecting user to OAuth setup")
redirect(location: "https://cloud.lifx.com/oauth/authorize?${toQueryString(oauthParams)}")
}
@@ -265,23 +266,27 @@ def updated() {
}
def uninstalled() {
log.info("Uninstalling, removing child devices...")
unschedule('updateDevices')
removeChildDevices(getChildDevices())
}
private removeChildDevices(devices) {
devices.each {
deleteChildDevice(it.deviceNetworkId) // 'it' is default
}
cirrus.unregisterServiceManager()
}
// called after Done is hit after selecting a Location
def initialize() {
log.debug "initialize"
updateDevices()
// Check for new devices and remove old ones every 3 hours
runEvery5Minutes('updateDevices')
if (cirrusEnabled) {
// Create the devices
updateDevicesFromResponse(devicesInLocation())
// Sync with Cirrus once per day to ensure consistency and maintain polling by Gadfly
runDaily(new Date(), registerWithCirrus)
}
else {
// Create the devices and generate events for their initial state
updateDevices()
// Check for new devices and remove old ones every 3 hours
runEvery5Minutes('updateDevices')
}
setupDeviceWatch()
}
@@ -305,8 +310,8 @@ Map apiRequestHeaders() {
// Requests
def logResponse(response) {
log.info("Status: ${response.status}")
log.info("Body: ${response.data}")
log.debug("Status: ${response.status}")
log.debug("Body: ${response.data}")
}
// API Requests
@@ -377,8 +382,58 @@ def devicesInLocation() {
return devicesList("location_id:${settings.selectedLocationId}")
}
// ensures the devices list is up to date
def updateDevices() {
def webhookCallback() {
log.debug "webhookCallback"
def data = request.JSON
log.debug data
if (data) {
updateDevicesFromResponse(data)
[status: "ok", source: "smartApp"]
}
else {
[status: "operation not defined", source: "smartApp"]
}
}
// Cirrus version that only creates and deletes devices, since Cirrus and Gadfly are responsible for updating
void updateDevicesFromResponse(devices) {
log.debug("updateDevicesFromResponse(${devices.size()})")
def changed = false
def deviceIds = []
def children = getChildDevices()
devices.each { device ->
deviceIds << device.id
def childDevice = children.find {it.deviceNetworkId == device.id}
if (!childDevice) {
log.trace "adding child device $device.label"
if (device.product.capabilities.has_color) {
addChildDevice(app.namespace, "LIFX Color Bulb", device.id, null, ["label": device.label, "completedSetup": true])
} else {
addChildDevice(app.namespace, "LIFX White Bulb", device.id, null, ["label": device.label, "completedSetup": true])
}
changed = true
}
}
children.findAll { !deviceIds.contains(it.deviceNetworkId) }.each {
log.trace "deleting child device $it.label"
deleteChildDevice(it.deviceNetworkId, true)
changed = true
}
if (changed) {
// Run in a separate sandbox instance because caching issues can prevent children from being picked up
runIn(1, registerWithCirrus)
}
}
// Non-Cirrus version that updates devices and generates events
void updateDevices() {
if (cirrusEnabled) {
switchToCirrus()
return
}
if (!state.devices) {
state.devices = [:]
}
@@ -428,7 +483,7 @@ def updateDevices() {
state.devices[device.id] = [online: device.connected]
}
getChildDevices().findAll { !selectors.contains("${it.deviceNetworkId}") }.each {
log.info("Deleting ${it.deviceNetworkId}")
log.debug("Deleting ${it.deviceNetworkId}")
if (state.devices[it.deviceNetworkId])
state.devices[it.deviceNetworkId] = null
// The reason the implementation is trying to delete this bulb is because it is not longer connected to the LIFX location.
@@ -441,3 +496,23 @@ def updateDevices() {
}
}
}
boolean getCirrusEnabled() {
def result = cirrus.enabled("smartthings.cdh.handlers.LifxLightHandler")
log.debug "cirrusEnabled=$result"
result
}
void switchToCirrus() {
log.info "Switching to cirrus"
registerWithCirrus()
unschedule()
runDaily(new Date(), registerWithCirrus)
}
def registerWithCirrus() {
cirrus.registerServiceManager("smartthings.cdh.handlers.LifxLightHandler", [
remoteAuthToken: state.lifxAccessToken,
lifxLocationId: settings.selectedLocationId,
])
}