diff --git a/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy b/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy index ea57db9..69d26f7 100644 --- a/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy +++ b/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy @@ -97,6 +97,7 @@ def refresh() { } def healthPoll() { + log.debug "healthPoll()" def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh() cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))} } diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index 1b8d775..37cbb85 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -655,55 +655,60 @@ void lowerSetpoint() { void alterSetpoint(temp) { def mode = device.currentValue("thermostatMode") - def heatingSetpoint = device.currentValue("heatingSetpoint") - def coolingSetpoint = device.currentValue("coolingSetpoint") - def deviceId = device.deviceNetworkId.split(/\./).last() - def targetHeatingSetpoint - def targetCoolingSetpoint - - //step1: check thermostatMode, enforce limits before sending request to cloud - if (mode == "heat" || mode == "auxHeatOnly"){ - if (temp.value > coolingSetpoint){ - targetHeatingSetpoint = temp.value - targetCoolingSetpoint = temp.value - } else { - targetHeatingSetpoint = temp.value - targetCoolingSetpoint = coolingSetpoint - } - } else if (mode == "cool") { - //enforce limits before sending request to cloud - if (temp.value < heatingSetpoint){ - targetHeatingSetpoint = temp.value - targetCoolingSetpoint = temp.value - } else { - targetHeatingSetpoint = heatingSetpoint - targetCoolingSetpoint = temp.value - } - } - - log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to $targetHeatingSetpoint " + - "coolingSetpoint to $targetCoolingSetpoint with holdType : ${holdType}" - - def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite" - - def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint - def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint - - if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) { - sendEvent("name": "thermostatSetpoint", "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}" + if (mode == "off" || mode == "auto") { + log.warn "this mode: $mode does not allow alterSetpoint" } else { - log.error "Error alterSetpoint()" + def heatingSetpoint = device.currentValue("heatingSetpoint") + def coolingSetpoint = device.currentValue("coolingSetpoint") + def deviceId = device.deviceNetworkId.split(/\./).last() + + def targetHeatingSetpoint + def targetCoolingSetpoint + + //step1: check thermostatMode, enforce limits before sending request to cloud if (mode == "heat" || mode == "auxHeatOnly"){ - sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false) + if (temp.value > coolingSetpoint){ + targetHeatingSetpoint = temp.value + targetCoolingSetpoint = temp.value + } else { + targetHeatingSetpoint = temp.value + targetCoolingSetpoint = coolingSetpoint + } } else if (mode == "cool") { - sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false) + //enforce limits before sending request to cloud + if (temp.value < heatingSetpoint){ + targetHeatingSetpoint = temp.value + targetCoolingSetpoint = temp.value + } else { + targetHeatingSetpoint = heatingSetpoint + targetCoolingSetpoint = temp.value + } } + + log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to $targetHeatingSetpoint " + + "coolingSetpoint to $targetCoolingSetpoint with holdType : ${holdType}" + + def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite" + + def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint + def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint + + if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) { + sendEvent("name": "thermostatSetpoint", "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"){ + sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false) + } else if (mode == "cool") { + sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false) + } + } + generateStatusEvent() } - generateStatusEvent() } def generateStatusEvent() { diff --git a/devicetypes/smartthings/nyce-open-closed-sensor.src/.st-ignore b/devicetypes/smartthings/nyce-open-closed-sensor.src/.st-ignore new file mode 100644 index 0000000..f78b46e --- /dev/null +++ b/devicetypes/smartthings/nyce-open-closed-sensor.src/.st-ignore @@ -0,0 +1,2 @@ +.st-ignore +README.md diff --git a/devicetypes/smartthings/nyce-open-closed-sensor.src/README.md b/devicetypes/smartthings/nyce-open-closed-sensor.src/README.md new file mode 100644 index 0000000..64624e3 --- /dev/null +++ b/devicetypes/smartthings/nyce-open-closed-sensor.src/README.md @@ -0,0 +1,37 @@ +# Nyce Door/Window Sensor (Open/Close Sensor) + + + +Works with: + +* [NYCE Door/Window Sensor NCZ-3011](https://support.smartthings.com/hc/en-us/articles/204576764-NYCE-Door-Window-Sensor) + +## Table of contents + +* [Capabilities](#capabilities) +* [Health](#device-health) +* [Battery](#battery-specification) +* [Troubleshooting](#troubleshooting) + +## Capabilities + +* **Configuration** - _configure()_ command called when device is installed or device preferences updated +* **Contact Sensor** - can detect contact (with possible values - open/closed) +* **Battery** - defines device uses a battery +* **Refresh** - _refresh()_ command for status updates +* **Health Check** - indicates ability to get device health notifications + +## Device Health + +A Category C2 Nyce Door/Window sensor that has 12min check-in interval + +## Battery Specification + +One 3V CR2032 battery required. + +## 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 sensor from SmartThings can be found in the following link: +* [Nyce Door/Window Sensor](https://support.smartthings.com/hc/en-us/articles/204576764-NYCE-Door-Window-Sensor) diff --git a/devicetypes/smartthings/nyce-open-closed-sensor.src/nyce-open-closed-sensor.groovy b/devicetypes/smartthings/nyce-open-closed-sensor.src/nyce-open-closed-sensor.groovy index 506ca89..7f75068 100644 --- a/devicetypes/smartthings/nyce-open-closed-sensor.src/nyce-open-closed-sensor.groovy +++ b/devicetypes/smartthings/nyce-open-closed-sensor.src/nyce-open-closed-sensor.groovy @@ -19,25 +19,26 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus metadata { definition (name: "NYCE Open/Closed Sensor", namespace: "smartthings", author: "NYCE") { - capability "Battery" + capability "Battery" capability "Configuration" - capability "Contact Sensor" + capability "Contact Sensor" capability "Refresh" - - command "enrollResponse" - - - fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3010", deviceJoinName: "NYCE Door Hinge Sensor" + capability "Health Check" + + command "enrollResponse" + + + fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3010", deviceJoinName: "NYCE Door Hinge Sensor" fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor" - fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor" - fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor" - fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor" + fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor" + fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor" + fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor" } - + simulator { - + } - + tiles { standardTile("contact", "device.contact", width: 2, height: 2) { state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e") @@ -273,23 +274,28 @@ private List parseIasMessage(String description) { return resultListMap } +/** + * PING is used by Device-Watch in attempt to reach the Device + * */ +def ping() { + return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level +} + def configure() { + // Device-Watch allows 2 check-in misses from device + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) - def configCmds = [ - //battery reporting and heartbeat - "zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 1 {${device.zigbeeId}} {}", "delay 200", - "zcl global send-me-a-report 1 0x20 0x20 600 3600 {01}", "delay 200", - "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500", - - + def enrollCmds = [ // Writes CIE attribute on end device to direct reports to the hub's EUID "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 1", "delay 500", ] log.debug "configure: Write IAS CIE" - return configCmds + // battery minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity + return enrollCmds + zigbee.batteryConfig(30, 300) + refresh() // send refresh cmds as part of config } def enrollResponse() { @@ -334,7 +340,8 @@ Integer convertHexToInt(hex) { def refresh() { log.debug "Refreshing Battery" - [ + def refreshCmds = [ "st rattr 0x${device.deviceNetworkId} ${endpointId} 1 0x20", "delay 200" ] + return refreshCmds + enrollResponse() } 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 a90ce2e..98f75a0 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 @@ -16,7 +16,7 @@ import physicalgraph.zigbee.clusters.iaszone.ZoneStatus metadata { - definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") { + definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings") { capability "Battery" capability "Configuration" capability "Contact Sensor" diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index d793cf4..d39d451 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -37,7 +37,10 @@ preferences { def mainPage() { def bridges = bridgesDiscovered() - if (state.username && bridges) { + + if (state.refreshUsernameNeeded) { + return bridgeLinking() + } else if (state.username && bridges) { return bulbDiscovery() } else { return bridgeDiscovery() @@ -102,13 +105,22 @@ def bridgeLinking() { def nextPage = "" def title = "Linking with your Hue" - def paragraphText + def paragraphText if (selectedHue) { + if (state.refreshUsernameNeeded) { + paragraphText = "The current Hue username is invalid.\n\nPlease press the button on your Hue Bridge to re-link. " + } else { paragraphText = "Press the button on your Hue Bridge to setup a link. " - } else { - paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next." - } + } + } else { + paragraphText = "You haven't selected a Hue Bridge, please Press \"Done\" and select one before clicking next." + } if (state.username) { //if discovery worked + if (state.refreshUsernameNeeded) { + state.refreshUsernameNeeded = false + // Issue one poll with new username to cancel local polling with old username + poll() + } nextPage = "bulbDiscovery" title = "Success!" paragraphText = "Linking to your hub was a success! Please click 'Next'!" @@ -915,8 +927,15 @@ private handleCommandResponse(body) { updates[childDeviceNetworkId]."$eventType" = v } } - } else if (payload.error) { - log.warn "Error returned from Hue bridge error = ${body?.error}" + } else if (payload?.error) { + log.warn "Error returned from Hue bridge, error = ${payload?.error}" + // Check for unauthorized user + if (payload?.error?.type?.value == 1) { + log.error "Hue username is not valid" + state.refreshUsernameNeeded = true + state.username = null + } + return [] } }