diff --git a/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/.st-ignore b/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/.st-ignore new file mode 100644 index 0000000..71af75c --- /dev/null +++ b/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md \ No newline at end of file diff --git a/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/README.md b/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/README.md new file mode 100644 index 0000000..aaaf3d2 --- /dev/null +++ b/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/README.md @@ -0,0 +1,41 @@ +# Fibaro Door Window Sensor ZW5 + +Cloud Execution + +Works with: + +* [Fibaro Door/Window Sensor ZW5](https://www.smartthings.com/works-with-smartthings/sensors/fibaro-doorwindow-sensor) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Battery** - defines device uses a battery +* **Contact Sensor** - can detect contact (possible values: open,closed) +* **Sensor** - detects sensor events +* **Tamper Alert** - detects tampers +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Fibaro Door/Window Sensor ZW5 is a Z-wave sleepy device and wakes up every 4 hours. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins. + +* __482min__ checkInterval + +## Battery Specification + +One 1/2AA 3.6V battery is required. + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: +* [Fibaro Door/Window Sensor ZW5 Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204075194-Fibaro-Door-Window-Sensor) \ No newline at end of file diff --git a/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/fibaro-door-window-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/fibaro-door-window-sensor-zw5.groovy index b05d959..95adeb5 100644 --- a/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/fibaro-door-window-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-door-window-sensor-zw5.src/fibaro-door-window-sensor-zw5.groovy @@ -20,6 +20,7 @@ metadata { capability "Sensor" capability "Configuration" capability "Tamper Alert" + capability "Health Check" fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x85, 0x59, 0x22, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x2B, 0x9C, 0x30, 0x86, 0x84", outClusters: "" } @@ -199,7 +200,9 @@ def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLoca def configure() { log.debug "Executing '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]) + def cmds = [] cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGK's default wake up interval diff --git a/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy index c9c5a30..5e66488 100644 --- a/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-flood-sensor-zw5.src/fibaro-flood-sensor-zw5.groovy @@ -34,13 +34,13 @@ metadata { tiles(scale: 2) { multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app tileAttribute("device.water", key:"PRIMARY_CONTROL") { - attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#00a0dc") - attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#e86d13") + attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff") + attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#00a0dc") } tileAttribute("device.tamper", key:"SECONDARY_CONTROL") { - attributeState("active", label:'tamper active', backgroundColor:"#00a0dc") - attributeState("inactive", label:'tamper inactive', backgroundColor:"#cccccc") + attributeState("active", label:'tamper active', backgroundColor:"#cccccc") + attributeState("inactive", label:'tamper inactive', backgroundColor:"#00A0DC") } } diff --git a/devicetypes/keen-home/keen-home-smart-vent.src/.st-ignore b/devicetypes/keen-home/keen-home-smart-vent.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/keen-home/keen-home-smart-vent.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/keen-home/keen-home-smart-vent.src/README.md b/devicetypes/keen-home/keen-home-smart-vent.src/README.md new file mode 100644 index 0000000..355c2b2 --- /dev/null +++ b/devicetypes/keen-home/keen-home-smart-vent.src/README.md @@ -0,0 +1,39 @@ +# Keen Home Smart Vent + +Cloud Execution + +Works with: + +* [Keen Home Smart Vent](https://www.smartthings.com/works-with-smartthings/keen-home/keen-home-smart-vent) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Troubleshooting](#Troubleshooting) + +## Capabilities + +* **Switch** - can detect state (possible values: on/off) +* **Switch Level** - represents current light level, usually 0-100 in percent +* **Sensor** - detects sensor events +* **Temperature Measurement** - represents capability to measure temperature +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Battery** - defines device uses a battery +* **Refresh** - _refresh()_ command for status updates +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Keen Home Smart Vent with reporting interval of 10 mins. +SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE` + +* __22min__ checkInterval + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range. +Pairing needs to be tried again by placing the sensor closer to the hub. +Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links +for the different models: +* [Keen Home Smart Vent Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205302050-Keen-Home-Smart-Vent) diff --git a/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy b/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy index 93703ef..ee9e703 100644 --- a/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy +++ b/devicetypes/keen-home/keen-home-smart-vent.src/keen-home-smart-vent.groovy @@ -11,6 +11,7 @@ metadata { capability "Sensor" capability "Temperature Measurement" capability "Battery" + // capability "Health Check" command "getLevel" command "getOnOff" @@ -20,10 +21,7 @@ metadata { command "setZigBeeIdTile" command "clearObstruction" - fingerprint endpoint: "1", - profileId: "0104", - inClusters: "0000,0001,0003,0004,0005,0006,0008,0020,0402,0403,0B05,FC01,FC02", - outClusters: "0019" + fingerprint endpoint: "1", profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0006,0008,0020,0402,0403,0B05,FC01,FC02", outClusters: "0019" } // simulator metadata @@ -466,15 +464,27 @@ def refresh() { getBattery() } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return refresh() +} + def configure() { log.debug "CONFIGURE" + // Device-Watch allows 2 check-in misses from device + ping (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + // sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // get ZigBee ID by hidden tile because that's the only way we can do it setZigBeeIdTile() def configCmds = [ // bind reporting clusters to hub - "zdo bind 0x${device.deviceNetworkId} 1 1 0x0006 {${device.zigbeeId}} {}", "delay 500", + //commenting out switch cluster bind as using wrapper onOffConfig of zigbee class + //"zdo bind 0x${device.deviceNetworkId} 1 1 0x0006 {${device.zigbeeId}} {}", "delay 500", "zdo bind 0x${device.deviceNetworkId} 1 1 0x0008 {${device.zigbeeId}} {}", "delay 500", "zdo bind 0x${device.deviceNetworkId} 1 1 0x0402 {${device.zigbeeId}} {}", "delay 500", "zdo bind 0x${device.deviceNetworkId} 1 1 0x0403 {${device.zigbeeId}} {}", "delay 500", @@ -510,5 +520,5 @@ def configure() { // "send 0x${device.deviceNetworkId} 1 1", "delay 1500", ] - return configCmds + refresh() + return configCmds + zigbee.onOffConfig() + refresh() } diff --git a/devicetypes/smartthings/bose-soundtouch.src/bose-soundtouch.groovy b/devicetypes/smartthings/bose-soundtouch.src/bose-soundtouch.groovy index 5aafc08..ff6a8ee 100644 --- a/devicetypes/smartthings/bose-soundtouch.src/bose-soundtouch.groovy +++ b/devicetypes/smartthings/bose-soundtouch.src/bose-soundtouch.groovy @@ -28,6 +28,8 @@ metadata { capability "Refresh" capability "Music Player" capability "Health Check" + capability "Sensor" + capability "Actuator" /** * Define all commands, ie, if you have a custom action not diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index 0f281a2..b200e94 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -32,7 +32,7 @@ metadata { command "switchMode" command "switchFanMode" - attribute "thermostatSetpoint", "number" + attribute "displayThermostatSetpoint", "string" // Added to be able to show "Auto"/"Off" keeping attribute thermostatSetpoint a number attribute "thermostatStatus", "string" attribute "maxHeatingSetpoint", "number" attribute "minHeatingSetpoint", "number" @@ -43,8 +43,8 @@ metadata { } tiles { - valueTile("temperature", "device.temperature", width: 2, height: 2) { - state("temperature", label:'${currentValue}°', unit:"F", + standardTile("temperature", "device.temperature", width: 2, height: 2, decoration: "flat") { + state("temperature", label:'${currentValue}°', unit:"F", icon: "st.thermostat.ac.air-conditioning", backgroundColors:[ // Celsius [value: 0, color: "#153591"], @@ -70,7 +70,7 @@ metadata { state "heat", action:"switchMode", nextState: "updating", icon: "st.thermostat.heat" state "cool", action:"switchMode", nextState: "updating", icon: "st.thermostat.cool" state "auto", action:"switchMode", nextState: "updating", icon: "st.thermostat.auto" - state "auxHeatOnly", action:"switchMode", icon: "st.thermostat.emergency-heat" + state "emergency heat", action:"switchMode", icon: "st.thermostat.emergency-heat" // emergency heat = auxHeatOnly state "updating", label:"Working", icon: "st.secondary.secondary" } standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") { @@ -81,8 +81,8 @@ metadata { standardTile("upButtonControl", "device.thermostatSetpoint", inactiveLabel: false, decoration: "flat") { state "setpoint", action:"raiseSetpoint", icon:"st.thermostat.thermostat-up" } - valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") { - state "thermostatSetpoint", label:'${currentValue}' + valueTile("displayThermostatSetpoint", "device.displayThermostatSetpoint", width: 1, height: 1, decoration: "flat") { + state "displayThermostatSetpoint", label:'${currentValue}' } valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") { state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff" @@ -113,7 +113,7 @@ metadata { state "humidity", label:'${currentValue}%' } main "temperature" - details(["temperature", "upButtonControl", "thermostatSetpoint", "currentStatus", "downButtonControl", "mode", "fanMode","humidity", "resumeProgram", "refresh"]) + details(["temperature", "upButtonControl", "displayThermostatSetpoint", "currentStatus", "downButtonControl", "mode", "fanMode","humidity", "resumeProgram", "refresh"]) } preferences { @@ -185,6 +185,11 @@ def generateEvent(Map results) { isChange = isStateChange(device, name, value.toString()) event['isStateChange'] = isChange event['displayed'] = false + } else if (name == "thermostatMode") { + def mode = value.toString() + mode = (mode == "auxHeatOnly") ? "emergency heat" : mode + isChange = isStateChange(device, name, mode) + event << [value: mode, isStateChange: isChange, displayed: isDisplayed] } else { isChange = isStateChange(device, name, value.toString()) isDisplayed = isChange @@ -452,10 +457,10 @@ def emergencyHeat() { } def auxHeatOnly() { - log.debug "auxHeatOnly" + log.debug "auxHeatOnly = emergency heat" def deviceId = device.deviceNetworkId.split(/\./).last() if (parent.setMode ("auxHeatOnly", deviceId)) - generateModeEvent("auxHeatOnly") + generateModeEvent("emergency heat") // emergency heat = auxHeatOnly else { log.debug "Error setting new mode." def currentMode = device.currentState("thermostatMode")?.value @@ -574,17 +579,23 @@ def generateSetpointEvent() { sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale) + def averageSetpoint = roundC((heatingSetpoint + coolingSetpoint) / 2) if (mode == "heat") { sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) + sendEvent("name":"displayThermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale, displayed: false) } else if (mode == "cool") { sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale) + sendEvent("name":"displayThermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale, displayed: false) } else if (mode == "auto") { - sendEvent("name":"thermostatSetpoint", "value":"Auto") + sendEvent("name":"thermostatSetpoint", "value":averageSetpoint, "unit":location.temperatureScale) + sendEvent("name":"displayThermostatSetpoint", "value":"Auto", displayed: false) } else if (mode == "off") { - sendEvent("name":"thermostatSetpoint", "value":"Off") - } else if (mode == "auxHeatOnly") { + sendEvent("name":"thermostatSetpoint", "value":averageSetpoint, "unit":location.temperatureScale) + sendEvent("name":"displayThermostatSetpoint", "value":"Off", displayed: false) + } else if (mode == "emergency heat") { // emergency heat = auxHeatOnly sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) + sendEvent("name":"displayThermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale, displayed: false) } } @@ -621,13 +632,14 @@ void raiseSetpoint() { targetvalue = thermostatSetpoint ? thermostatSetpoint : 0 targetvalue = location.temperatureScale == "F"? targetvalue + 1 : targetvalue + 0.5 - if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue > maxHeatingSetpoint) { + if ((mode == "heat" || mode == "emergency heat") && targetvalue > maxHeatingSetpoint) { // emergency heat = auxHeatOnly targetvalue = maxHeatingSetpoint } else if (mode == "cool" && targetvalue > maxCoolingSetpoint) { targetvalue = maxCoolingSetpoint } sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false) + sendEvent("name":"displayThermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false) log.info "In mode $mode raiseSetpoint() to $targetvalue" runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite @@ -666,13 +678,14 @@ void lowerSetpoint() { targetvalue = thermostatSetpoint ? thermostatSetpoint : 0 targetvalue = location.temperatureScale == "F"? targetvalue - 1 : targetvalue - 0.5 - if ((mode == "heat" || mode == "auxHeatOnly") && targetvalue < minHeatingSetpoint) { + if ((mode == "heat" || mode == "emergency heat") && targetvalue < minHeatingSetpoint) { // emergency heat = auxHeatOnly targetvalue = minHeatingSetpoint } else if (mode == "cool" && targetvalue < minCoolingSetpoint) { targetvalue = minCoolingSetpoint } sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false) + sendEvent("name":"displayThermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false) log.info "In mode $mode lowerSetpoint() to $targetvalue" runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite @@ -706,7 +719,7 @@ void alterSetpoint(temp) { } //step1: check thermostatMode, enforce limits before sending request to cloud - if (mode == "heat" || mode == "auxHeatOnly"){ + if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly if (temp.value > coolingSetpoint){ targetHeatingSetpoint = temp.value targetCoolingSetpoint = temp.value @@ -735,15 +748,18 @@ void alterSetpoint(temp) { if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) { sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false) + sendEvent("name": "displayThermostatSetpoint", "value": temp.value, displayed: false) sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint, "unit": location.temperatureScale) sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint, "unit": location.temperatureScale) log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}" } else { log.error "Error alterSetpoint()" - if (mode == "heat" || mode == "auxHeatOnly"){ + if (mode == "heat" || mode == "emergency heat"){ // emergency heat = auxHeatOnly sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false) + sendEvent("name": "displayThermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false) } else if (mode == "cool") { sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false) + sendEvent("name": "displayThermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false) } } @@ -759,6 +775,7 @@ def generateStatusEvent() { def coolingSetpoint = device.currentValue("coolingSetpoint") def temperature = device.currentValue("temperature") def statusText + def operatingState = "idle" log.debug "Generate Status Event for Mode = ${mode}" log.debug "Temperature = ${temperature}" @@ -767,20 +784,29 @@ def generateStatusEvent() { log.debug "HVAC Mode = ${mode}" if (mode == "heat") { - if (temperature >= heatingSetpoint) + if (temperature >= heatingSetpoint) { statusText = "Right Now: Idle" - else + } else { statusText = "Heating to ${heatingSetpoint} ${location.temperatureScale}" + operatingState = "heating" + } } else if (mode == "cool") { - if (temperature <= coolingSetpoint) + if (temperature <= coolingSetpoint) { statusText = "Right Now: Idle" - else + } else { statusText = "Cooling to ${coolingSetpoint} ${location.temperatureScale}" + operatingState = "cooling" + } } else if (mode == "auto") { statusText = "Right Now: Auto" + if (temperature < heatingSetpoint) { + operatingState = "heating" + } else if (temperature > coolingSetpoint) { + operatingState = "cooling" + } } else if (mode == "off") { statusText = "Right Now: Off" - } else if (mode == "auxHeatOnly") { + } else if (mode == "emergency heat") { // emergency heat = auxHeatOnly statusText = "Emergency Heat" } else { statusText = "?" @@ -788,6 +814,7 @@ def generateStatusEvent() { log.debug "Generate Status Event = ${statusText}" sendEvent("name":"thermostatStatus", "value":statusText, "description":statusText, displayed: true) + sendEvent("name":"thermostatOperatingState", "value":operatingState, "description":operatingState, displayed: false) } def generateActivityFeedsEvent(notificationMessage) { diff --git a/devicetypes/smartthings/econet-vent.src/.st-ignore b/devicetypes/smartthings/econet-vent.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/econet-vent.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/econet-vent.src/README.md b/devicetypes/smartthings/econet-vent.src/README.md new file mode 100644 index 0000000..b680629 --- /dev/null +++ b/devicetypes/smartthings/econet-vent.src/README.md @@ -0,0 +1,43 @@ +# EcoNet Vent + +Cloud Execution + +Works with: + +* [EcoNet Controls Z-Wave Vent](https://www.smartthings.com/works-with-smartthings/econet-controls/econet-controls-z-wave-vent) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Switch Level** - allows for the control of the level attribute of a light +* **Actuator** - represents that a Device has commands +* **Switch** - allows for the control of a switch device +* **Battery** - defines that the device has a battery +* **Refresh** - _refresh()_ command for status updates +* **Sensor** - detects sensor events +* **Polling** - allows for the polling of devices that support it +* **Configuration** - allow configuration of devices that support it +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +EcoNet Controls Z-Wave Vent is polled by the hub. +As of hubCore version 0.14.38 the hub sends up reports every 15 minutes regardless of whether the state changed. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins. +Not to mention after going OFFLINE when the device is plugged back in, it might take a considerable amount of time for +the device to appear as ONLINE again. This is because if this listening device does not respond to two poll requests in a row, +it is not polled for 5 minutes by the hub. This can delay up the process of being marked ONLINE by quite some time. + +* __32min__ checkInterval + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: +* [EcoNet Controls Z-Wave Vent Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204556420-EcoNet-EV100-Vent) \ No newline at end of file diff --git a/devicetypes/smartthings/econet-vent.src/econet-vent.groovy b/devicetypes/smartthings/econet-vent.src/econet-vent.groovy index 56a3249..344f1b5 100644 --- a/devicetypes/smartthings/econet-vent.src/econet-vent.groovy +++ b/devicetypes/smartthings/econet-vent.src/econet-vent.groovy @@ -26,11 +26,13 @@ metadata { capability "Sensor" capability "Polling" capability "Configuration" + // capability "Health Check" command "open" command "close" fingerprint deviceId: "0x1100", inClusters: "0x26,0x72,0x86,0x77,0x80,0x20" + // fingerprint mfr:"0157", prod:"0100", model:"0100", deviceJoinName: "EcoNet Controls Z-Wave Vent" } simulator { @@ -85,6 +87,8 @@ def parse(String description) { //send the command to stop polling def updated() { + // 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]) response("poll stop") } @@ -169,6 +173,13 @@ def setLevel(value, duration) { setLevel(value) } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + refresh() +} + def refresh() { delayBetween([ zwave.switchMultilevelV1.switchMultilevelGet().format(), diff --git a/devicetypes/smartthings/everspring-flood-sensor.src/.st-ignore b/devicetypes/smartthings/everspring-flood-sensor.src/.st-ignore new file mode 100644 index 0000000..71af75c --- /dev/null +++ b/devicetypes/smartthings/everspring-flood-sensor.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md \ No newline at end of file diff --git a/devicetypes/smartthings/everspring-flood-sensor.src/README.md b/devicetypes/smartthings/everspring-flood-sensor.src/README.md new file mode 100644 index 0000000..b8e5bde --- /dev/null +++ b/devicetypes/smartthings/everspring-flood-sensor.src/README.md @@ -0,0 +1,40 @@ +# Everspring Flood Sensor + +Cloud Execution + +Works with: + +* [Everspring Water Detector](https://www.smartthings.com/works-with-smartthings/sensors/everspring-water-detector) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Water Sensor** - can detect presence of water (dry or wet) +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Sensor** - detects sensor events +* **Battery** - defines device uses a battery +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Everspring Water Detector is a Z-wave sleepy device and wakes up every 4 hours. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins. + +* __482min__ checkInterval + +## Battery Specification + +Three AA 1.5V batteries are required. + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: +* [Everspring Water Detector Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202088304-Everspring-Water-Detector) \ No newline at end of file diff --git a/devicetypes/smartthings/everspring-flood-sensor.src/everspring-flood-sensor.groovy b/devicetypes/smartthings/everspring-flood-sensor.src/everspring-flood-sensor.groovy index 3c72f29..29e1bf5 100644 --- a/devicetypes/smartthings/everspring-flood-sensor.src/everspring-flood-sensor.groovy +++ b/devicetypes/smartthings/everspring-flood-sensor.src/everspring-flood-sensor.groovy @@ -17,6 +17,7 @@ metadata { capability "Configuration" capability "Sensor" capability "Battery" + capability "Health Check" fingerprint deviceId: "0xA102", inClusters: "0x86,0x72,0x85,0x84,0x80,0x70,0x9C,0x20,0x71" } @@ -138,6 +139,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]) if (!device.currentState("battery")) { sendEvent(name: "battery", value:100, unit:"%", descriptionText:"(Default battery event)", displayed:false) } diff --git a/devicetypes/smartthings/fibaro-door-window-sensor.src/.st-ignore b/devicetypes/smartthings/fibaro-door-window-sensor.src/.st-ignore new file mode 100644 index 0000000..71af75c --- /dev/null +++ b/devicetypes/smartthings/fibaro-door-window-sensor.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md \ No newline at end of file diff --git a/devicetypes/smartthings/fibaro-door-window-sensor.src/README.md b/devicetypes/smartthings/fibaro-door-window-sensor.src/README.md new file mode 100644 index 0000000..b425636 --- /dev/null +++ b/devicetypes/smartthings/fibaro-door-window-sensor.src/README.md @@ -0,0 +1,40 @@ +# Fibaro Door Window Sensor + +Cloud Execution + +Works with: + +* [Fibaro Door/Window Sensor](https://www.smartthings.com/works-with-smartthings/sensors/fibaro-doorwindow-sensor) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Contact Sensor** - can detect contact (possible values: open,closed) +* **Sensor** - detects sensor events +* **Battery** - defines device uses a battery +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Fibaro Door/Window Sensor is a Z-wave sleepy device and wakes up every 4 hours. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins. + +* __482min__ checkInterval + +## Battery Specification + +One 1/2AA 3.6V battery is required. + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: +* [Fibaro Door/Window Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204075194-Fibaro-Door-Window-Sensor) \ No newline at end of file diff --git a/devicetypes/smartthings/fibaro-door-window-sensor.src/fibaro-door-window-sensor.groovy b/devicetypes/smartthings/fibaro-door-window-sensor.src/fibaro-door-window-sensor.groovy index 8970554..4ece9ab 100644 --- a/devicetypes/smartthings/fibaro-door-window-sensor.src/fibaro-door-window-sensor.groovy +++ b/devicetypes/smartthings/fibaro-door-window-sensor.src/fibaro-door-window-sensor.groovy @@ -39,7 +39,8 @@ capability "Contact Sensor" capability "Sensor" capability "Battery" - capability "Configuration" + capability "Configuration" + capability "Health Check" command "resetParams2StDefaults" command "listCurrentParams" @@ -266,6 +267,9 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS */ def configure() { log.debug "Configuring Device..." + // 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]) + def cmds = [] cmds << zwave.configurationV1.configurationSet(configurationValue: [0,0], parameterNumber: 1, size: 2).format() // send associate to group 3 to get sensor data reported only to hub diff --git a/devicetypes/smartthings/plant-link.src/.st-ignore b/devicetypes/smartthings/plant-link.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/plant-link.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/plant-link.src/README.md b/devicetypes/smartthings/plant-link.src/README.md new file mode 100644 index 0000000..8ec24e4 --- /dev/null +++ b/devicetypes/smartthings/plant-link.src/README.md @@ -0,0 +1,35 @@ +# Plant Link + +Cloud Execution + +Works with: + +* [OSO Technologies PlantLink Soil Moisture Sensor](https://www.smartthings.com/works-with-smartthings/oso-technologies/oso-technologies-plantlink-soil-moisture-sensor) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Relative Humidity Measurement** - allows reading the relative humidity from devices that support it +* **Sensor** - detects sensor events +* **Battery** - defines device uses a battery +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Plant Link sensor is a Z-wave sleepy device and checks in every 15 minutes. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*15 + 2)mins = 32 mins. + +* __32min__ checkInterval + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range. +Pairing needs to be tried again by placing the sensor closer to the hub. +Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links +for the different models: +* [OSO Technologies PlantLink Soil Moisture Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/206868986-PlantLink-Soil-Moisture-Sensor) diff --git a/devicetypes/smartthings/plant-link.src/plant-link.groovy b/devicetypes/smartthings/plant-link.src/plant-link.groovy index 275880b..e3114e3 100644 --- a/devicetypes/smartthings/plant-link.src/plant-link.groovy +++ b/devicetypes/smartthings/plant-link.src/plant-link.groovy @@ -21,8 +21,10 @@ metadata { capability "Relative Humidity Measurement" capability "Battery" capability "Sensor" + capability "Health Check" fingerprint profileId: "0104", inClusters: "0000,0003,0405,FC08", outClusters: "0003" + fingerprint endpoint: "1", profileId: "0104", inClusters: "0000,0001,0003,0B04", outClusters: "0003", manufacturer: "", model: "", deviceJoinName: "OSO Technologies PlantLink Soil Moisture Sensor" } tiles { @@ -48,6 +50,11 @@ metadata { } } +def updated() { + // Device-Watch allows 2 check-in misses from device + sendEvent(name: "checkInterval", value: 2 * 15 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) +} + // Parse incoming device messages to generate events def parse(String description) { log.debug "Parse description $description" diff --git a/devicetypes/smartthings/smartsense-moisture.src/.st-ignore b/devicetypes/smartthings/smartsense-moisture.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/smartsense-moisture.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/smartsense-moisture.src/README.md b/devicetypes/smartthings/smartsense-moisture.src/README.md new file mode 100644 index 0000000..35f05aa --- /dev/null +++ b/devicetypes/smartthings/smartsense-moisture.src/README.md @@ -0,0 +1,36 @@ +# Smartsense Moisture + +Cloud Execution + +Works with: + +* [FortrezZ Moisture Sensor](https://www.smartthings.com/works-with-smartthings/fortrezz/fortrezz-moisture-sensor) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Water Sensor** - can detect presence of water (dry or wet) +* **Sensor** - detects sensor events +* **Battery** - defines device uses a battery +* **Temperature Measurement** - represents capability to measure temperature +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Smartsense Moisture is a Z-wave sleepy device type and checks in every 4 hours. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins. + +* __482min__ checkInterval + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range. +Pairing needs to be tried again by placing the sensor closer to the hub. +Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links +for the different models: +* [FortrezZ Moisture Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200930740-FortrezZ-Moisture-Sensor) diff --git a/devicetypes/smartthings/smartsense-moisture.src/smartsense-moisture.groovy b/devicetypes/smartthings/smartsense-moisture.src/smartsense-moisture.groovy index ebd5888..e2d78f4 100644 --- a/devicetypes/smartthings/smartsense-moisture.src/smartsense-moisture.groovy +++ b/devicetypes/smartthings/smartsense-moisture.src/smartsense-moisture.groovy @@ -17,9 +17,11 @@ metadata { capability "Sensor" capability "Battery" capability "Temperature Measurement" + capability "Health Check" fingerprint deviceId: "0x2001", inClusters: "0x30,0x9C,0x9D,0x85,0x80,0x72,0x31,0x84,0x86" fingerprint deviceId: "0x2101", inClusters: "0x71,0x70,0x85,0x80,0x72,0x31,0x84,0x86" + fingerprint mfr:"0084", prod:"0063", model:"010C", deviceJoinName: "FortrezZ Moisture Sensor" } simulator { @@ -89,6 +91,11 @@ def parse(String description) { return result } +def updated() { + // Device-Watch simply pings if no device events received for 482min(checkInterval) + sendEvent(name: "checkInterval", value: 2 * 4 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) +} + def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) { [descriptionText: "${device.displayName} woke up", isStateChange: false] diff --git a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy index 328c9a7..c22162b 100644 --- a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy +++ b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy @@ -52,7 +52,7 @@ metadata { tiles(scale: 2) { multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4){ tileAttribute ("device.contact", key: "PRIMARY_CONTROL") { - attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"##e86d13" + attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#e86d13" attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#00a0dc" } } diff --git a/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy b/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy index 305d3fe..8bc2cfa 100644 --- a/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy +++ b/devicetypes/smartthings/tyco-door-window-sensor.src/tyco-door-window-sensor.groovy @@ -23,6 +23,7 @@ metadata { capability "Refresh" capability "Temperature Measurement" capability "Health Check" + capability "Sensor" command "enrollResponse" diff --git a/devicetypes/smartthings/zigbee-switch.src/.st-ignore b/devicetypes/smartthings/zigbee-switch.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/zigbee-switch.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/zigbee-switch.src/README.md b/devicetypes/smartthings/zigbee-switch.src/README.md new file mode 100644 index 0000000..be8c067 --- /dev/null +++ b/devicetypes/smartthings/zigbee-switch.src/README.md @@ -0,0 +1,35 @@ +# Leviton Switch (ZigBee) + +Cloud Execution + +Works with: + +* [Leviton Switch (ZigBee)](https://www.smartthings.com/works-with-smartthings/leviton/leviton-switch) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Troubleshooting](#Troubleshooting) + +## Capabilities + +* **Actuator** - represents that a Device has commands +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Refresh** - _refresh()_ command for status updates +* **Switch** - can detect state (possible values: on/off) +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +A Zigbee Switch with reporting interval of 10 mins. +SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE` + +* __22min__ checkInterval + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: +* [Leviton Switch Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/209686003-How-to-connect-Leviton-ZigBee-devices) diff --git a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy index 52621a8..94dc7e7 100644 --- a/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy +++ b/devicetypes/smartthings/zigbee-switch.src/zigbee-switch.groovy @@ -18,6 +18,7 @@ metadata { capability "Configuration" capability "Refresh" capability "Switch" + capability "Health Check" fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006" fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0003, 0006, 0019, 0406", manufacturer: "Leviton", model: "ZSS-10", deviceJoinName: "Leviton Switch" @@ -75,11 +76,20 @@ def on() { zigbee.on() } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return refresh() +} + def refresh() { zigbee.onOffRefresh() + zigbee.onOffConfig() } def configure() { + // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time) + sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." zigbee.onOffRefresh() + zigbee.onOffConfig() } diff --git a/devicetypes/smartthings/zooz-power-strip-outlet.src/zooz-power-strip-outlet.groovy b/devicetypes/smartthings/zooz-power-strip-outlet.src/zooz-power-strip-outlet.groovy new file mode 100644 index 0000000..0fd103c --- /dev/null +++ b/devicetypes/smartthings/zooz-power-strip-outlet.src/zooz-power-strip-outlet.groovy @@ -0,0 +1,41 @@ +/** + * Zooz Power Strip Outlet + * + * Copyright 2017 SmartThings + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License + * for the specific language governing permissions and limitations under the License. + * + */ +metadata { + definition (name: "Zooz Power Strip Outlet", namespace: "smartthings", author: "SmartThings") { + capability "Switch" + capability "Actuator" + capability "Sensor" + } + + tiles { + multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ + tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { + attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"turningOn" + attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"turningOff" + attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff" + attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" + } + } + } +} + +void on() { + parent.childOn(device.deviceNetworkId) +} + +void off() { + parent.childOff(device.deviceNetworkId) +} diff --git a/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy b/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy new file mode 100644 index 0000000..5647939 --- /dev/null +++ b/devicetypes/smartthings/zooz-power-strip.src/zooz-power-strip.groovy @@ -0,0 +1,217 @@ +/** + * Zooz ZEN20 Power Strip Outlet + * + * Implementation of the Zooz ZEN20 power strip that uses the new composite device capabilities to provide individual + * control of each outlet from SmartApps as well as the mobile app. Incorporates contributions from: + * + * Eric Maycock (https://github.com/erocm123/SmartThingsPublic/blob/master/devicetypes/erocm123/zooz-power-strip.src/zooz-power-strip.groovy) + * Robert Vandervoort (https://github.com/robertvandervoort/SmartThings/blob/master/zooZ-Strip-ZEN20/device_type-zooZ-strip-ZEN20_v1.0) + * + * Copyright 2017 SmartThings + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License + * for the specific language governing permissions and limitations under the License. + * + */ +metadata { + definition (name: "Zooz Power Strip", namespace: "smartthings", author: "SmartThings") { + capability "Switch" + capability "Refresh" + capability "Actuator" + capability "Sensor" + capability "Configuration" + + fingerprint manufacturer: "015D", prod: "0651", model: "F51C", deviceJoinName: "Zooz ZEN 20 Power Strip" + } + + tiles { + multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ + tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { + attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState:"turningOn" + attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00A0DC", nextState:"turningOff" + attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#00A0DC", nextState:"turningOff" + attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" + } + } + childDeviceTiles("outlets") + standardTile("refresh", "device.switch", width: 1, height: 1, inactiveLabel: false, decoration: "flat") { + state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" + } + } +} + +///////////////////////////// +// Installation and update // +///////////////////////////// +def installed() { + createChildDevices() +} + +def updated() { + if (!childDevices) { + createChildDevices() + } + else if (device.label != state.oldLabel) { + childDevices.each { + def newLabel = "${device.displayName} (CH${channelNumber(it.deviceNetworkId)})" + it.setLabel(newLabel) + } + state.oldLabel = device.label + } +} + +def configure() { + refresh() +} + + +////////////////////// +// Event Generation // +////////////////////// +def parse(String description) { + trace "parse('$description')" + def result = [] + if (description.startsWith("Err")) { + result = createEvent(descriptionText:description, isStateChange:true) + } else if (description != "updated") { + def cmd = zwave.parse(description, [0x60: 3, 0x32: 3, 0x25: 1, 0x20: 1]) + if (cmd) { + result += zwaveEvent(cmd, 1) + } + else { + log.warn "Unparsed description $description" + } + } + result +} + +def zwaveEvent(physicalgraph.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd, ep) { + def encapsulatedCommand = cmd.encapsulatedCommand([0x32: 3, 0x25: 1, 0x20: 1]) + if (encapsulatedCommand) { + zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer) + } +} + +def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, endpoint) { + trace "zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport $cmd, $endpoint)" + zwaveBinaryEvent(cmd, endpoint) +} + +def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, endpoint) { + trace "zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport $cmd, $endpoint)" + zwaveBinaryEvent(cmd, endpoint) +} + +def zwaveBinaryEvent(cmd, endpoint) { + def result = [] + def children = childDevices + def childDevice = children.find{it.deviceNetworkId.endsWith("$endpoint")} + if (childDevice) { + childDevice.sendEvent(name: "switch", value: cmd.value ? "on" : "off") + + if (cmd.value) { + // One on and the strip is on + result << createEvent(name: "switch", value: "on") + } else { + // All off and the strip is off + if (!children.any { it.currentValue("switch") == "on" }) { + result << createEvent(name: "switch", value: "off") + } + } + } + result +} + +def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd, ep) { + updateDataValue("MSR", String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)) + return null +} + +def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd, ep) { + trace "applicationVersion $cmd.applicationVersion" +} + +def zwaveEvent(physicalgraph.zwave.Command cmd, ep) { + log.warn("${device.displayName}: Unhandled ${cmd}" + (ep ? " from endpoint $ep" : "")) +} + +///////////////////////////// +// Installation and update // +///////////////////////////// +def on() { + def cmds = [] + def cmd = zwave.switchBinaryV1.switchBinarySet(switchValue: 0xFF) + cmds << zwave.multiChannelV3.multiChannelCmdEncap(bitAddress: true, destinationEndPoint:0x1F).encapsulate(cmd).format() + cmds << "delay 400" + cmds.addAll(refresh()) + return cmds +} + +def off() { + def cmds = [] + def cmd = zwave.switchBinaryV1.switchBinarySet(switchValue: 0x00) + cmds << zwave.multiChannelV3.multiChannelCmdEncap(bitAddress: true, destinationEndPoint:0x1F).encapsulate(cmd).format() + cmds << "delay 400" + cmds.addAll(refresh()) + return cmds +} + +////////////////////// +// Child Device API // +////////////////////// +void childOn(String dni) { + onOffCmd(0xFF, channelNumber(dni)) +} + +void childOff(String dni) { + onOffCmd(0, channelNumber(dni)) +} + +def refresh() { + def cmds = (1..5).collect { endpoint -> + encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint) + } + delayBetween(cmds, 100) +} + +/////////////////// +// Local Methods // +/////////////////// +private channelNumber(String dni) { + dni.split("-ep")[-1] as Integer +} + +private void onOffCmd(value, endpoint = null) { + def actions = [ + new physicalgraph.device.HubAction(encap(zwave.basicV1.basicSet(value: value), endpoint)), + new physicalgraph.device.HubAction(encap(zwave.switchBinaryV1.switchBinaryGet(), endpoint)), + ] + sendHubCommand(actions, 500) +} + +private void createChildDevices() { + state.oldLabel = device.label + for (i in 1..5) { + addChildDevice("Zooz Power Strip Outlet", "${device.deviceNetworkId}-ep${i}", null, + [completedSetup: true, label: "${device.displayName} (CH${i})", + isComponent: true, componentName: "ch$i", componentLabel: "Channel $i"]) + } +} + +private encap(cmd, endpoint) { + if (endpoint) { + zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint:endpoint).encapsulate(cmd).format() + } else { + cmd.format() + } +} + +private trace(msg) { + //log.trace(msg) +} diff --git a/devicetypes/smartthings/zwave-door-window-sensor.src/.st-ignore b/devicetypes/smartthings/zwave-door-window-sensor.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/zwave-door-window-sensor.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/zwave-door-window-sensor.src/README.md b/devicetypes/smartthings/zwave-door-window-sensor.src/README.md new file mode 100644 index 0000000..d97e2ae --- /dev/null +++ b/devicetypes/smartthings/zwave-door-window-sensor.src/README.md @@ -0,0 +1,36 @@ +# Z-Wave Door Window Sensor + +Cloud Execution + +Works with: + +* [Aeon Labs Door/Window Sensor (Gen 5)](https://www.smartthings.com/works-with-smartthings/aeon-labs/aeon-labs-doorwindow-sensor-gen-5) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Troubleshooting](#Troubleshooting) + +## Capabilities + +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Health Check** - indicates ability to get device health notifications +* **Sensor** - detects sensor events +* **Battery** - defines that the device has a battery +* **Contact Sensor** - allows reading the value of a contact sensor device + +## Device Health + +Z-Wave Door Window Sensor is a Z-wave sleepy device and checks in every 4 hours. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins. + +* __482min__ checkInterval + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following links +for the different models: +* [Aeon Labs Door/Window Sensor (Gen 5) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/211834163-How-to-connect-Aeon-Labs-door-window-sensors) \ No newline at end of file diff --git a/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy b/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy index ff8491d..0f751e9 100644 --- a/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy +++ b/devicetypes/smartthings/zwave-door-window-sensor.src/zwave-door-window-sensor.groovy @@ -22,12 +22,14 @@ metadata { capability "Sensor" capability "Battery" capability "Configuration" + capability "Health Check" fingerprint deviceId: "0x2001", inClusters: "0x30,0x80,0x84,0x85,0x86,0x72" fingerprint deviceId: "0x07", inClusters: "0x30" fingerprint deviceId: "0x0701", inClusters: "0x5E,0x98" fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82" fingerprint deviceId: "0x0701", inClusters: "0x5E,0x80,0x71,0x85,0x70,0x72,0x86,0x30,0x31,0x84,0x59,0x73,0x5A,0x8F,0x98,0x7A", outClusters:"0x20" // Philio multi+ + fingerprint mfr:"0086", prod:"0002", model:"001D", deviceJoinName: "Aeon Labs Door/Window Sensor (Gen 5)" } // simulator metadata @@ -78,6 +80,8 @@ def parse(String description) { } def updated() { + // Device-Watch simply pings if no device events received for 482min(checkInterval) + sendEvent(name: "checkInterval", value: 2 * 4 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID]) def cmds = [] if (!state.MSR) { cmds = [ diff --git a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy index 4b2fba2..03664f3 100644 --- a/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy +++ b/devicetypes/smartthings/zwave-metering-dimmer.src/zwave-metering-dimmer.groovy @@ -16,7 +16,7 @@ * */ metadata { - definition (name: "Z-Wave Metering Dimmer", namespace: "smartthings", author: "SmartThings") { + definition (name: "Z-Wave Metering Dimmer", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") { capability "Switch" capability "Polling" capability "Power Meter" diff --git a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy index d8892e4..5f1d0c9 100644 --- a/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy +++ b/devicetypes/smartthings/zwave-metering-switch.src/zwave-metering-switch.groovy @@ -12,7 +12,7 @@ * */ metadata { - definition (name: "Z-Wave Metering Switch", namespace: "smartthings", author: "SmartThings") { + definition (name: "Z-Wave Metering Switch", namespace: "smartthings", author: "SmartThings", ocfDeviceType: "oic.d.switch") { capability "Energy Meter" capability "Actuator" capability "Switch" @@ -22,10 +22,12 @@ metadata { capability "Configuration" capability "Sensor" capability "Light" + capability "Health Check" command "reset" fingerprint inClusters: "0x25,0x32" + fingerprint mfr:"0086", prod:"0003", model:"0012", deviceJoinName: "Aeon Labs Micro Smart Switch" } // simulator metadata @@ -73,6 +75,8 @@ metadata { } def updated() { + // 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]) try { if (!state.MSR) { response(zwave.manufacturerSpecificV2.manufacturerSpecificGet().format()) @@ -82,7 +86,7 @@ def updated() { def parse(String description) { def result = null - if(description == "updated") return + if(description == "updated") return def cmd = zwave.parse(description, [0x20: 1, 0x32: 1, 0x72: 2]) if (cmd) { result = zwaveEvent(cmd) @@ -179,6 +183,14 @@ def poll() { ]) } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + log.debug "ping() called" + refresh() +} + def refresh() { delayBetween([ zwave.switchBinaryV1.switchBinaryGet().format(), diff --git a/devicetypes/smartthings/zwave-motion-sensor.src/.st-ignore b/devicetypes/smartthings/zwave-motion-sensor.src/.st-ignore new file mode 100644 index 0000000..71af75c --- /dev/null +++ b/devicetypes/smartthings/zwave-motion-sensor.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md \ No newline at end of file diff --git a/devicetypes/smartthings/zwave-motion-sensor.src/README.md b/devicetypes/smartthings/zwave-motion-sensor.src/README.md new file mode 100644 index 0000000..f698d03 --- /dev/null +++ b/devicetypes/smartthings/zwave-motion-sensor.src/README.md @@ -0,0 +1,39 @@ +# Z-wave Motion Sensor + +Cloud Execution + +Works with: + +* [Ecolink PIR Motion Detector with Pet Immunity](https://www.smartthings.com/works-with-smartthings/sensors/ecolink-pir-motion-detector-with-pet-immunity) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Motion Sensor** - can detect motion +* **Sensor** - detects sensor events +* **Battery** - defines device uses a battery +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Ecolink PIR Motion Detector with Pet Immunity is a Z-wave sleepy device and wakes up every 4 hours. +Device-Watch allows 2 check-in misses from device plus some lag time. So Check-in interval = (2*4*60 + 2)mins = 482 mins. + +* __482min__ checkInterval + +## Battery Specification + +One CR123A Lithium 3V battery is required. + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link: +* [Ecolink PIR Motion Detector with Pet Immunity Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202294400-Ecolink-PIR-Motion-Detector-PIRZWAVE2-ECO-) \ No newline at end of file diff --git a/devicetypes/smartthings/zwave-motion-sensor.src/zwave-motion-sensor.groovy b/devicetypes/smartthings/zwave-motion-sensor.src/zwave-motion-sensor.groovy index a5003c3..2395602 100644 --- a/devicetypes/smartthings/zwave-motion-sensor.src/zwave-motion-sensor.groovy +++ b/devicetypes/smartthings/zwave-motion-sensor.src/zwave-motion-sensor.groovy @@ -21,6 +21,7 @@ metadata { capability "Motion Sensor" capability "Sensor" capability "Battery" + capability "Health Check" fingerprint mfr: "011F", prod: "0001", model: "0001", deviceJoinName: "Schlage Motion Sensor" // Schlage motion fingerprint mfr: "014A", prod: "0001", model: "0001", deviceJoinName: "Ecolink Motion Sensor" // Ecolink motion @@ -50,6 +51,11 @@ metadata { } } +def updated(){ +// 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]) +} + def parse(String description) { def result = null if (description.startsWith("Err")) { diff --git a/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy b/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy index 404882a..69c6c20 100644 --- a/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy +++ b/devicetypes/smartthings/zwave-thermostat.src/zwave-thermostat.groovy @@ -20,6 +20,7 @@ metadata { capability "Configuration" capability "Polling" capability "Sensor" + capability "Health Check" attribute "thermostatFanState", "string" @@ -30,6 +31,7 @@ metadata { fingerprint deviceId: "0x08" fingerprint inClusters: "0x43,0x40,0x44,0x31" + fingerprint mfr:"0039", prod:"0011", model:"0001", deviceJoinName: "Honeywell Z-Wave Thermostat" } // simulator metadata @@ -40,9 +42,9 @@ metadata { status "auto" : "command: 4003, payload: 03" status "emergencyHeat" : "command: 4003, payload: 04" - status "fanAuto" : "command: 4403, payload: 00" - status "fanOn" : "command: 4403, payload: 01" - status "fanCirculate" : "command: 4403, payload: 06" + status "auto" : "command: 4403, payload: 00" // "fanAuto" + status "on" : "command: 4403, payload: 01" // "fanOn" + status "circulate" : "command: 4403, payload: 06" // "fanCirculate status "heat 60" : "command: 4303, payload: 01 09 3C" status "heat 68" : "command: 4303, payload: 01 09 44" @@ -72,8 +74,9 @@ metadata { } tiles { - valueTile("temperature", "device.temperature", width: 2, height: 2) { - state("temperature", label:'${currentValue}°', + // Using standardTile instead of valueTile as it renders the icon better + standardTile("temperature", "device.temperature", width: 2, height: 2) { + state("temperature", label:'${currentValue}°', icon: "st.thermostat.ac.air-conditioning", backgroundColors:[ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], @@ -86,19 +89,20 @@ metadata { ) } standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { - state "off", label:'${name}', action:"switchMode", nextState:"to_heat" - state "heat", label:'${name}', action:"switchMode", nextState:"to_cool" - state "cool", label:'${name}', action:"switchMode", nextState:"..." - state "auto", label:'${name}', action:"switchMode", nextState:"..." - state "emergency heat", label:'${name}', action:"switchMode", nextState:"..." - state "to_heat", label: "heat", action:"switchMode", nextState:"to_cool" - state "to_cool", label: "cool", action:"switchMode", nextState:"..." + state "off", action:"switchMode", nextState:"to_heat", icon: "st.thermostat.heating-cooling-off" + state "heat", action:"switchMode", nextState:"to_cool", icon: "st.thermostat.heat" + state "cool", action:"switchMode", nextState:"...", icon: "st.thermostat.cool" + state "auto", action:"switchMode", nextState:"...", icon: "st.thermostat.auto" + state "emergency heat", action:"switchMode", nextState:"...", icon: "st.thermostat.emergency-heat" + state "to_heat", action:"switchMode", nextState:"to_cool", icon: "st.thermostat.heat" + state "to_cool", action:"switchMode", nextState:"...", icon: "st.thermostat.cool" state "...", label: "...", action:"off", nextState:"off" } standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") { - state "fanAuto", label:'${name}', action:"switchFanMode" - state "fanOn", label:'${name}', action:"switchFanMode" - state "fanCirculate", label:'${name}', action:"switchFanMode" + state "auto", action:"switchFanMode", nextState:"...", icon: "st.thermostat.fan-auto" // "fanAuto" + state "on", action:"switchFanMode", nextState:"...", icon: "st.thermostat.fan-on" // "fanOn" + state "circulate", action:"switchFanMode", nextState:"...", icon: "st.thermostat.fan-circulate" // "fanCirculate" + state "...", label: "...", nextState:"..." } controlTile("heatSliderControl", "device.heatingSetpoint", "slider", height: 1, width: 2, inactiveLabel: false) { state "setHeatingSetpoint", action:"quickSetHeat", backgroundColor:"#d04e00" @@ -115,14 +119,16 @@ metadata { standardTile("refresh", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { state "default", action:"polling.poll", icon:"st.secondary.refresh" } - standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") { - state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure" - } main "temperature" - details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh", "configure"]) + details(["temperature", "mode", "fanMode", "heatSliderControl", "heatingSetpoint", "coolSliderControl", "coolingSetpoint", "refresh"]) } } +def updated(){ + // 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]) +} + def parse(String description) { def map = createEvent(zwaveEvent(zwave.parse(description, [0x42:1, 0x43:2, 0x31: 3]))) @@ -279,13 +285,13 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanMod def map = [:] switch (cmd.fanMode) { case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_AUTO_LOW: - map.value = "fanAuto" + map.value = "auto" // "fanAuto" break case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_LOW: - map.value = "fanOn" + map.value = "on" // "fanOn" break case physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeReport.FAN_MODE_CIRCULATION: - map.value = "fanCirculate" + map.value = "circulate" // "fanCirculate" break } map.name = "thermostatFanMode" @@ -302,15 +308,19 @@ def zwaveEvent(physicalgraph.zwave.commands.thermostatmodev2.ThermostatModeSuppo if(cmd.auto) { supportedModes += "auto " } state.supportedModes = supportedModes + // No events to be generated, return empty map + return [:] } def zwaveEvent(physicalgraph.zwave.commands.thermostatfanmodev3.ThermostatFanModeSupportedReport cmd) { def supportedFanModes = "" - if(cmd.auto) { supportedFanModes += "fanAuto " } - if(cmd.low) { supportedFanModes += "fanOn " } - if(cmd.circulation) { supportedFanModes += "fanCirculate " } + if(cmd.auto) { supportedFanModes += "auto " } // "fanAuto " + if(cmd.low) { supportedFanModes += "on " } // "fanOn" + if(cmd.circulation) { supportedFanModes += "circulate " } // "fanCirculate" state.supportedFanModes = supportedFanModes + // No events to be generated, return empty map + return [:] } def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) { @@ -393,11 +403,25 @@ def setCoolingSetpoint(Double degrees, Integer delay = 30000) { ], delay) } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + log.debug "ping() called" + poll() +} + def configure() { delayBetween([ zwave.thermostatModeV2.thermostatModeSupportedGet().format(), zwave.thermostatFanModeV3.thermostatFanModeSupportedGet().format(), - zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format() + zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId]).format(), + zwave.sensorMultilevelV3.sensorMultilevelGet().format(), // current temperature + zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 1).format(), + zwave.thermostatSetpointV1.thermostatSetpointGet(setpointType: 2).format(), + zwave.thermostatModeV2.thermostatModeGet().format(), + zwave.thermostatFanModeV3.thermostatFanModeGet().format(), + zwave.thermostatOperatingStateV1.thermostatOperatingStateGet().format() ], 2300) } @@ -438,11 +462,11 @@ def switchToMode(nextMode) { def switchFanMode() { def currentMode = device.currentState("thermostatFanMode")?.value def lastTriedMode = state.lastTriedFanMode ?: currentMode ?: "off" - def supportedModes = getDataByName("supportedFanModes") ?: "fanAuto fanOn" - def modeOrder = ["fanAuto", "fanCirculate", "fanOn"] + def supportedModes = getDataByName("supportedFanModes") ?: "auto on" // "fanAuto fanOn" + def modeOrder = ["auto", "circulate", "on"] // "fanAuto", "fanCirculate", "fanOn" def next = { modeOrder[modeOrder.indexOf(it) + 1] ?: modeOrder[0] } def nextMode = next(lastTriedMode) - while (!supportedModes?.contains(nextMode) && nextMode != "fanAuto") { + while (!supportedModes?.contains(nextMode) && nextMode != "auto") { // "fanAuto" nextMode = next(nextMode) } switchToFanMode(nextMode) @@ -453,11 +477,11 @@ def switchToFanMode(nextMode) { if(supportedFanModes && !supportedFanModes.contains(nextMode)) log.warn "thermostat mode '$nextMode' is not supported" def returnCommand - if (nextMode == "fanAuto") { + if (nextMode == "auto") { // "fanAuto" returnCommand = fanAuto() - } else if (nextMode == "fanOn") { + } else if (nextMode == "on") { // "fanOn" returnCommand = fanOn() - } else if (nextMode == "fanCirculate") { + } else if (nextMode == "circulate") { // "fanCirculate" returnCommand = fanCirculate() } else { log.debug("no fan mode '$nextMode'") diff --git a/devicetypes/zenwithin/zen-thermostat.src/.st-ignore b/devicetypes/zenwithin/zen-thermostat.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/zenwithin/zen-thermostat.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/zenwithin/zen-thermostat.src/README.md b/devicetypes/zenwithin/zen-thermostat.src/README.md new file mode 100644 index 0000000..8864ff4 --- /dev/null +++ b/devicetypes/zenwithin/zen-thermostat.src/README.md @@ -0,0 +1,37 @@ +# Zen Thermostat + +Cloud Execution + +Works with: + +* [Zen Thermostat](https://www.smartthings.com/works-with-smartthings/zen/zen-thermostat) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) + +## Capabilities + +* **Actuator** - represents that a Device has commands +* **Thermostat** - allows for the control of a thermostat device +* **Temperature Measurement** - get the temperature from a Device that reports current temperature +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Refresh** - _refresh()_ command for status updates +* **Sensor** - it represents that a Device has attributes +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +Zen Thermostat with reporting interval of 5 mins. +SmartThings platform will ping the device after `checkInterval` seconds of inactivity in last attempt to reach the device before marking it `OFFLINE` + +* __12min__ checkInterval + + +## Troubleshooting + +If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range. +Pairing needs to be tried again by placing the device closer to the hub. +Other troubleshooting tips are listed as follows: +* [Zen Thermostat Troubleshooting:](https://support.smartthings.com/hc/en-us/articles/204356564-Zen-Thermostat) diff --git a/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy b/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy index 2e046da..a3e54a9 100644 --- a/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy +++ b/devicetypes/zenwithin/zen-thermostat.src/zen-thermostat.groovy @@ -12,8 +12,9 @@ metadata { capability "Configuration" capability "Refresh" capability "Sensor" - - fingerprint profileId: "0104", endpointId: "01", inClusters: "0000,0001,0003,0004,0005,0020,0201,0202,0204,0B05", outClusters: "000A, 0019" + capability "Health Check" + + fingerprint profileId: "0104", endpointId: "01", inClusters: "0000,0001,0003,0004,0005,0020,0201,0202,0204,0B05", outClusters: "000A, 0019", manufacturer: "Zen Within", model: "Zen-01", deviceJoinName: "Zen Thermostat" //attribute "temperatureUnit", "number" @@ -467,8 +468,12 @@ def fanAuto() { "st wattr 0x${device.deviceNetworkId} 1 0x202 0 0x30 {05}" } - - +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + refresh() +} // =============== SmartThings Default Fucntions: refresh, configure, poll =============== def refresh() @@ -502,6 +507,7 @@ def poll() def configure() { + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "configure() - binding & attribute report" [ //Set long poll interval to 2 qs @@ -511,7 +517,7 @@ def configure() //Thermostat - Cluster 201 "zdo bind 0x${device.deviceNetworkId} 1 1 0x201 {${device.zigbeeId}} {}", "delay 500", - "zcl global send-me-a-report 0x201 0 0x29 5 300 {3200}", + "zcl global send-me-a-report 0x201 0 0x29 5 300 {3200}", "send 0x${device.deviceNetworkId} 1 1", "delay 500", "zcl global send-me-a-report 0x201 0x0011 0x29 5 300 {3200}", diff --git a/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy b/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy index 974561e..dd17429 100644 --- a/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy +++ b/smartapps/smartthings/lifx-connect.src/lifx-connect.groovy @@ -19,8 +19,15 @@ definition( singleInstance: true) { appSetting "clientId" appSetting "clientSecret" + appSetting "serverUrl" // See note below } +// NOTE regarding OAuth settings. On NA01 (i.e. graph.api), NA01S, and NA01D the serverUrl app setting can be left +// Blank. For other shards is should be set to the callback URL registered with LIFX, which is: +// +// Production -- https://graph.api.smartthings.com +// Staging -- https://graph-na01s-useast1.smartthingsgdev.com +// Development -- https://graph-na01d-useast1.smartthingsgdev.com preferences { page(name: "Credentials", title: "LIFX", content: "authPage", install: true) @@ -35,8 +42,8 @@ mappings { path("/test") { action: [ GET: "oauthSuccess" ] } } -def getServerUrl() { return "https://graph.api.smartthings.com" } -def getCallbackUrl() { return "https://graph.api.smartthings.com/oauth/callback"} +def getServerUrl() { return appSettings.serverUrl ?: apiServerUrl } +def getCallbackUrl() { return "${getServerUrl()}/oauth/callback" } def apiURL(path = '/') { return "https://api.lifx.com/v1${path}" } def getSecretKey() { return appSettings.secretKey } def getClientId() { return appSettings.clientId }