diff --git a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy index 7504d93..ae882ed 100644 --- a/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy +++ b/devicetypes/fibargroup/fibaro-motion-sensor-zw5.src/fibaro-motion-sensor-zw5.groovy @@ -127,9 +127,10 @@ def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelR def map = [ displayed: true ] switch (cmd.sensorType) { case 1: - map.name = "temperature" - map.unit = cmd.scale == 1 ? "F" : "C" - map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, map.unit, cmd.precision) + def cmdScale = cmd.scale == 1 ? "F" : "C" + map.name = "temperature" + map.unit = getTemperatureScale() + map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision) break case 3: map.name = "illuminance" diff --git a/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy b/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy index 2ea9bd4..69d26f7 100644 --- a/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy +++ b/devicetypes/smartthings/cree-bulb.src/cree-bulb.groovy @@ -93,7 +93,7 @@ def ping() { } def refresh() { - zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() } def healthPoll() { @@ -105,9 +105,7 @@ def healthPoll() { def configure() { unschedule() runEvery5Minutes("healthPoll") - log.debug "Configuring Reporting and Bindings." // Device-Watch allows 2 check-in misses from device sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - // minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity - zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffRefresh() + zigbee.levelRefresh() } diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index 358576c..ec7cdad 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -104,8 +104,21 @@ def parse(String description) { } } else { - log.warn "DID NOT PARSE MESSAGE for description : $description" - log.debug zigbee.parseDescriptionAsMap(description) + def cluster = zigbee.parse(description) + + if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07){ + if (cluster.data[0] == 0x00) { + log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } + else { + log.warn "DID NOT PARSE MESSAGE for description : $description" + log.debug "${cluster}" + } } } @@ -128,8 +141,10 @@ def refresh() { } 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]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity zigbee.onOffConfig(0, 300) + powerConfig() + refresh() } diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy index 8b51662..22a8a61 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy @@ -118,14 +118,28 @@ private Map parseCatchAllMessage(String description) { if (shouldProcessMessage(cluster)) { switch(cluster.clusterId) { case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) + // 0x07 - configure reporting + if (cluster.command != 0x07) { + resultMap = getBatteryResult(cluster.data.last()) + } break case 0x0402: - // temp is last 2 data values. reverse to swap endian - String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() - def value = getTemperature(temp) - resultMap = getTemperatureResult(value) + if (cluster.command == 0x07) { + if (cluster.data[0] == 0x00){ + log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } + else { + // temp is last 2 data values. reverse to swap endian + String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() + def value = getTemperature(temp) + resultMap = getTemperatureResult(value) + } break } } @@ -135,10 +149,8 @@ private Map parseCatchAllMessage(String description) { private boolean shouldProcessMessage(cluster) { // 0x0B is default response indicating message got through - // 0x07 is bind message boolean ignoredMessage = cluster.profileId != 0x0104 || cluster.command == 0x0B || - cluster.command == 0x07 || (cluster.data.size() > 0 && cluster.data.first() == 0x3e) return !ignoredMessage } @@ -292,8 +304,9 @@ def refresh() { } 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]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) log.debug "Configuring Reporting, IAS CIE, and Bindings." diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index 4293bfa..d885613 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -122,19 +122,37 @@ private Map parseCatchAllMessage(String description) { if (shouldProcessMessage(cluster)) { switch(cluster.clusterId) { case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) + // 0x07 - configure reporting + if (cluster.command != 0x07) { + resultMap = getBatteryResult(cluster.data.last()) + } break case 0x0402: - // temp is last 2 data values. reverse to swap endian - String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() - def value = getTemperature(temp) - resultMap = getTemperatureResult(value) + if (cluster.command == 0x07) { + if (cluster.data[0] == 0x00) { + log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + + } + else { + // temp is last 2 data values. reverse to swap endian + String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() + def value = getTemperature(temp) + resultMap = getTemperatureResult(value) + } break case 0x0406: - log.debug 'motion' - resultMap.name = 'motion' + // 0x07 - configure reporting + if (cluster.command != 0x07) { + log.debug 'motion' + resultMap.name = 'motion' + } break } } @@ -144,10 +162,8 @@ private Map parseCatchAllMessage(String description) { private boolean shouldProcessMessage(cluster) { // 0x0B is default response indicating message got through - // 0x07 is bind message boolean ignoredMessage = cluster.profileId != 0x0104 || cluster.command == 0x0B || - cluster.command == 0x07 || (cluster.data.size() > 0 && cluster.data.first() == 0x3e) return !ignoredMessage } @@ -303,8 +319,9 @@ def refresh() { } 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]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) log.debug "Configuring Reporting, IAS CIE, and Bindings." diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index 606cb01..9240ec5 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -147,20 +147,33 @@ private Map parseCatchAllMessage(String description) { if (shouldProcessMessage(cluster)) { switch(cluster.clusterId) { case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) + // 0x07 - configure reporting + if (cluster.command != 0x07) { + resultMap = getBatteryResult(cluster.data.last()) + } break case 0xFC02: - log.debug 'ACCELERATION' + log.debug 'ACCELERATION' break case 0x0402: - log.debug 'TEMP' - // temp is last 2 data values. reverse to swap endian - String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() - def value = getTemperature(temp) - resultMap = getTemperatureResult(value) - break + if (cluster.command == 0x07) { + if(cluster.data[0] == 0x00) { + log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } + else { + // temp is last 2 data values. reverse to swap endian + String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() + def value = getTemperature(temp) + resultMap = getTemperatureResult(value) + } + break } } @@ -169,10 +182,8 @@ private Map parseCatchAllMessage(String description) { private boolean shouldProcessMessage(cluster) { // 0x0B is default response indicating message got through - // 0x07 is bind message boolean ignoredMessage = cluster.profileId != 0x0104 || cluster.command == 0x0B || - cluster.command == 0x07 || (cluster.data.size() > 0 && cluster.data.first() == 0x3e) return !ignoredMessage } @@ -401,8 +412,9 @@ def refresh() { } 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]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting" diff --git a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy index d588dac..bda54e6 100644 --- a/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy +++ b/devicetypes/smartthings/smartsense-open-closed-sensor.src/smartsense-open-closed-sensor.groovy @@ -109,15 +109,28 @@ private Map parseCatchAllMessage(String description) { if (shouldProcessMessage(cluster)) { switch(cluster.clusterId) { case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) + // 0x07 - configure reporting + if (cluster.command != 0x07) { + resultMap = getBatteryResult(cluster.data.last()) + } break case 0x0402: - log.debug 'TEMP' - // temp is last 2 data values. reverse to swap endian - String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() - def value = getTemperature(temp) - resultMap = getTemperatureResult(value) + if (cluster.command == 0x07){ + if (cluster.data[0] == 0x00) { + log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } + else { + // temp is last 2 data values. reverse to swap endian + String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() + def value = getTemperature(temp) + resultMap = getTemperatureResult(value) + } break } } @@ -127,10 +140,8 @@ private Map parseCatchAllMessage(String description) { private boolean shouldProcessMessage(cluster) { // 0x0B is default response indicating message got through - // 0x07 is bind message boolean ignoredMessage = cluster.profileId != 0x0104 || cluster.command == 0x0B || - cluster.command == 0x07 || (cluster.data.size() > 0 && cluster.data.first() == 0x3e) return !ignoredMessage } @@ -255,8 +266,9 @@ def refresh() { } 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]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) log.debug "Configuring Reporting, IAS CIE, and Bindings." diff --git a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy index c83d433..ea94edb 100644 --- a/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy +++ b/devicetypes/smartthings/smartsense-temp-humidity-sensor.src/smartsense-temp-humidity-sensor.groovy @@ -93,20 +93,37 @@ private Map parseCatchAllMessage(String description) { if (shouldProcessMessage(cluster)) { switch(cluster.clusterId) { case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) + // 0x07 - configure reporting + if (cluster.command != 0x07) { + resultMap = getBatteryResult(cluster.data.last()) + } break case 0x0402: - // temp is last 2 data values. reverse to swap endian - String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() - def value = getTemperature(temp) - resultMap = getTemperatureResult(value) - break + if (cluster.command == 0x07) { + if (cluster.data[0] == 0x00){ + log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } + else { + // temp is last 2 data values. reverse to swap endian + String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() + def value = getTemperature(temp) + resultMap = getTemperatureResult(value) + } + break case 0xFC45: - String pctStr = cluster.data[-1, -2].collect { Integer.toHexString(it) }.join('') - String display = Math.round(Integer.valueOf(pctStr, 16) / 100) - resultMap = getHumidityResult(display) + // 0x07 - configure reporting + if (cluster.command != 0x07) { + String pctStr = cluster.data[-1, -2].collect { Integer.toHexString(it) }.join('') + String display = Math.round(Integer.valueOf(pctStr, 16) / 100) + resultMap = getHumidityResult(display) + } break } } @@ -116,10 +133,8 @@ private Map parseCatchAllMessage(String description) { private boolean shouldProcessMessage(cluster) { // 0x0B is default response indicating message got through - // 0x07 is bind message boolean ignoredMessage = cluster.profileId != 0x0104 || cluster.command == 0x0B || - cluster.command == 0x07 || (cluster.data.size() > 0 && cluster.data.first() == 0x3e) return !ignoredMessage } @@ -264,8 +279,9 @@ def refresh() } 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]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) log.debug "Configuring Reporting and Bindings." def humidityConfigCmds = [ diff --git a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy index e966b79..b0239e6 100644 --- a/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy +++ b/devicetypes/smartthings/zigbee-dimmer.src/zigbee-dimmer.groovy @@ -60,8 +60,21 @@ def parse(String description) { } } else { - log.warn "DID NOT PARSE MESSAGE for description : $description" - log.debug zigbee.parseDescriptionAsMap(description) + def cluster = zigbee.parse(description) + + if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) { + if (cluster.data[0] == 0x00) { + log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } + else { + log.warn "DID NOT PARSE MESSAGE for description : $description" + log.debug "${cluster}" + } } } @@ -84,13 +97,15 @@ def ping() { } def refresh() { - zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() } def configure() { log.debug "Configuring Reporting and Bindings." - // Device-Watch allows 2 check-in misses from device - sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() } diff --git a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy index f9a29b5..ab1f76a 100644 --- a/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy +++ b/devicetypes/smartthings/zigbee-rgbw-bulb.src/zigbee-rgbw-bulb.groovy @@ -95,7 +95,7 @@ def parse(String description) { } else { def zigbeeMap = zigbee.parseDescriptionAsMap(description) - log.trace "zigbeeMap : $zigbeeMap" + def cluster = zigbee.parse(description) if (zigbeeMap?.clusterInt == COLOR_CONTROL_CLUSTER) { if(zigbeeMap.attrInt == ATTRIBUTE_HUE){ //Hue Attribute @@ -107,8 +107,18 @@ def parse(String description) { sendEvent(name: "saturation", value: saturationValue, descriptionText: "Color has changed", displayed: false) } } + else if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) { + if (cluster.data[0] == 0x00){ + log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } else { log.info "DID NOT PARSE MESSAGE for description : $description" + log.debug zigbeeMap } } } @@ -128,13 +138,15 @@ def ping() { } def refresh() { - zigbee.onOffRefresh() + zigbee.readAttribute(0x0008, 0x00) + zigbee.readAttribute(0x0300, 0x00) + zigbee.readAttribute(0x0300, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(0x0300, ATTRIBUTE_HUE) + zigbee.readAttribute(0x0300, ATTRIBUTE_SATURATION) + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01) + zigbee.onOffRefresh() + zigbee.readAttribute(0x0008, 0x00) + zigbee.readAttribute(0x0300, 0x00) + zigbee.readAttribute(0x0300, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(0x0300, ATTRIBUTE_HUE) + zigbee.readAttribute(0x0300, ATTRIBUTE_SATURATION) + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01) } def configure() { log.debug "Configuring Reporting and Bindings." - // Device-Watch allows 2 check-in misses from device - sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE, 0x20, 1, 3600, 0x01) + zigbee.configureReporting(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION, 0x20, 1, 3600, 0x01) + zigbee.readAttribute(0x0006, 0x00) + zigbee.readAttribute(0x0008, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, 0x00) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_COLOR_TEMPERATURE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_HUE) + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) } @@ -177,5 +189,5 @@ def setHue(value) { def setSaturation(value) { def scaledSatValue = zigbee.convertToHexString(Math.round(value * 0xfe / 100.0), 2) - zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") //payload-> sat value, transition time + zigbee.command(COLOR_CONTROL_CLUSTER, SATURATION_COMMAND, scaledSatValue, "0500") + "delay 1000" + zigbee.readAttribute(COLOR_CONTROL_CLUSTER, ATTRIBUTE_SATURATION) } diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy index 16cd044..8a210f2 100644 --- a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy +++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy @@ -83,8 +83,21 @@ def parse(String description) { } } else { - log.warn "DID NOT PARSE MESSAGE for description : $description" - log.debug zigbee.parseDescriptionAsMap(description) + def cluster = zigbee.parse(description) + + if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07) { + if (cluster.data[0] == 0x00) { + log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster + sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + } + else { + log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + } + } + else { + log.warn "DID NOT PARSE MESSAGE for description : $description" + log.debug "${cluster}" + } } } @@ -108,13 +121,15 @@ def ping() { } def refresh() { - zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() } def configure() { log.debug "Configuring Reporting and Bindings." - // Device-Watch allows 2 check-in misses from device - sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // Device-Watch allows 3 check-in misses from device (plus 1 min lag time) + // enrolls with default periodic reporting until newer 5 min interval is confirmed + sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) + // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity zigbee.onOffConfig(0, 300) + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() } diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index a9f7566..d793cf4 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -1194,7 +1194,7 @@ private poll() { } private isOnline(id) { - return (state.bulbs[id].online != null && state.bulbs[id].online) || state.bulbs[id].online == null + return (state.bulbs[id]?.online != null && state.bulbs[id]?.online) || state.bulbs[id]?.online == null } private put(path, body) { @@ -1261,7 +1261,7 @@ def convertBulbListToMap() { try { if (state.bulbs instanceof java.util.List) { def map = [:] - state.bulbs.unique {it.id}.each { bulb -> + state.bulbs?.unique {it.id}.each { bulb -> map << ["${bulb.id}":["id":bulb.id, "name":bulb.name, "type": bulb.type, "modelid": bulb.modelid, "hub":bulb.hub, "online": bulb.online]] } state.bulbs = map diff --git a/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy b/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy index 0da1df6..3cf64e1 100644 --- a/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy +++ b/smartapps/smartthings/logitech-harmony-connect.src/logitech-harmony-connect.groovy @@ -97,7 +97,7 @@ def authPage() { def description = null if (!state.HarmonyAccessToken) { if (!state.accessToken) { - log.debug "About to create access token" + log.debug "Harmony - About to create access token" createAccessToken() } description = "Click to enter Harmony Credentials" @@ -141,13 +141,13 @@ def callback() { def redirectUrl = null if (params.authQueryString) { redirectUrl = URLDecoder.decode(params.authQueryString.replaceAll(".+&redirect_url=", "")) - log.debug "redirectUrl: ${redirectUrl}" + log.debug "Harmony - redirectUrl: ${redirectUrl}" } else { - log.warn "No authQueryString" + log.warn "Harmony - No authQueryString" } if (state.HarmonyAccessToken) { - log.debug "Access token already exists" + log.debug "Harmony - Access token already exists" discovery() success() } else { @@ -155,27 +155,27 @@ def callback() { if (code) { if (code.size() > 6) { // Harmony code - log.debug "Exchanging code for access token" + log.debug "Harmony - Exchanging code for access token" receiveToken(redirectUrl) } else { // Initiate the Harmony OAuth flow. init() } } else { - log.debug "This code should be unreachable" + log.debug "Harmony - This code should be unreachable" success() } } } def init() { - log.debug "Requesting Code" + log.debug "Harmony - Requesting Code" def oauthParams = [client_id: "${appSettings.clientId}", scope: "remote", response_type: "code", redirect_uri: "${servercallbackUrl}" ] redirect(location: "https://home.myharmony.com/oauth2/authorize?${toQueryString(oauthParams)}") } def receiveToken(redirectUrl = null) { - log.debug "receiveToken" + log.debug "Harmony - receiveToken" def oauthParams = [ client_id: "${appSettings.clientId}", client_secret: "${appSettings.clientSecret}", grant_type: "authorization_code", code: params.code ] def params = [ uri: "https://home.myharmony.com/oauth2/token?${toQueryString(oauthParams)}", @@ -186,7 +186,7 @@ def receiveToken(redirectUrl = null) { } } catch (java.util.concurrent.TimeoutException e) { fail(e) - log.warn "Connection timed out, please try again later." + log.warn "Harmony - Connection timed out, please try again later." } discovery() if (state.HarmonyAccessToken) { @@ -310,7 +310,7 @@ def buildRedirectUrl(page) { def installed() { if (!state.accessToken) { - log.debug "About to create access token" + log.debug "Harmony - About to create access token" createAccessToken() } else { initialize() @@ -319,7 +319,7 @@ def installed() { def updated() { if (!state.accessToken) { - log.debug "About to create access token" + log.debug "Harmony - About to create access token" createAccessToken() } else { initialize() @@ -330,9 +330,9 @@ def uninstalled() { if (state.HarmonyAccessToken) { try { state.HarmonyAccessToken = "" - log.debug "Success disconnecting Harmony from SmartThings" + log.debug "Harmony - Success disconnecting Harmony from SmartThings" } catch (groovyx.net.http.HttpResponseException e) { - log.error "Error disconnecting Harmony from SmartThings: ${e.statusCode}" + log.error "Harmony - Error disconnecting Harmony from SmartThings: ${e.statusCode}" } } } @@ -341,7 +341,8 @@ def initialize() { state.aux = 0 if (selectedhubs || selectedactivities) { addDevice() - runEvery5Minutes("poll") + runEvery5Minutes("poll") + getActivityList() } } @@ -350,7 +351,7 @@ def getHarmonydevices() { } Map discoverDevices() { - log.trace "Discovering devices..." + log.trace "Harmony - Discovering devices..." discovery() if (getHarmonydevices() != []) { def devices = state.Harmonydevices.hubs @@ -380,52 +381,52 @@ def discovery() { try { httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> if (response.status == 200) { - log.debug "valid Token" + log.debug "Harmony - valid Token" state.Harmonydevices = response.data state.resethub = false } else { - log.debug "Error: $response.status" + log.debug "Harmony - Error: $response.status" } } } catch (groovyx.net.http.HttpResponseException e) { if (e.statusCode == 401) { // token is expired state.remove("HarmonyAccessToken") - log.warn "Harmony Access token has expired" + log.warn "Harmony - Harmony Access token has expired" } } catch (java.net.SocketTimeoutException e) { - log.warn "Connection to the hub timed out. Please restart the hub and try again." + log.warn "Harmony - Connection to the hub timed out. Please restart the hub and try again." state.resethub = true } catch (e) { - log.info "Logitech Harmony - Error: $e" + log.info "Harmony - Error: $e" } return null } def addDevice() { - log.trace "Adding Hubs" + log.trace "Harmony - Adding Hubs" selectedhubs.each { dni -> def d = getChildDevice(dni) if(!d) { def newAction = state.HarmonyHubs.find { it.key == dni } d = addChildDevice("smartthings", "Logitech Harmony Hub C2C", dni, null, [label:"${newAction.value}"]) - log.trace "created ${d.displayName} with id $dni" + log.trace "Harmony - Created ${d.displayName} with id $dni" poll() } else { - log.trace "found ${d.displayName} with id $dni already exists" + log.trace "Harmony - Found ${d.displayName} with id $dni already exists" } } - log.trace "Adding Activities" + log.trace "Harmony - Adding Activities" selectedactivities.each { dni -> def d = getChildDevice(dni) if(!d) { def newAction = state.HarmonyActivities.find { it.key == dni } if (newAction) { d = addChildDevice("smartthings", "Harmony Activity", dni, null, [label:"${newAction.value} [Harmony Activity]"]) - log.trace "created ${d.displayName} with id $dni" + log.trace "Harmony - Created ${d.displayName} with id $dni" poll() } } else { - log.trace "found ${d.displayName} with id $dni already exists" + log.trace "Harmony - Found ${d.displayName} with id $dni already exists" } } } @@ -455,17 +456,17 @@ def activity(dni,mode) { def activityResponse(response, data) { if (response.hasError()) { - log.error "Logitech Harmony - response has error: $response.errorMessage" + log.error "Harmony - response has error: $response.errorMessage" if (response.status == 401) { // token is expired state.remove("HarmonyAccessToken") - log.warn "Logitech Harmony - Access token has expired" + log.warn "Harmony - Access token has expired" } } else { if (response.status == 200) { - log.trace "Command sent succesfully" + log.trace "Harmony - Command sent succesfully" poll() } else { - log.trace "Command failed. Error: $response.status" + log.trace "Harmony - Command failed. Error: $response.status" } } } @@ -481,16 +482,16 @@ def poll() { ] asynchttp_v1.get('pollResponse', params) } else { - log.warn "Logitech Harmony - Access token has expired" + log.warn "Harmony - Access token has expired" } } def pollResponse(response, data) { if (response.hasError()) { - log.error "Logitech Harmony - response has error: $response.errorMessage" + log.error "Harmony - response has error: $response.errorMessage" if (response.status == 401) { // token is expired state.remove("HarmonyAccessToken") - log.warn "Logitech Harmony - Access token has expired" + log.warn "Harmony - Access token has expired" } } else { def ResponseValues @@ -498,7 +499,7 @@ def pollResponse(response, data) { // json response already parsed into JSONElement object ResponseValues = response.json } catch (e) { - log.error "Logitech Harmony - error parsing json from response: $e" + log.error "Harmony - error parsing json from response: $e" } if (ResponseValues) { def map = [:] @@ -520,7 +521,7 @@ def pollResponse(response, data) { } } } else { - log.trace "Logitech Harmony - error response: $it.value.message" + log.trace "Harmony - error response: $it.value.message" } } def activities = getChildDevices() @@ -545,11 +546,43 @@ def pollResponse(response, data) { } } } else { - log.debug "Logitech Harmony - did not get json results from response body: $response.data" + log.debug "Harmony - did not get json results from response body: $response.data" } } } +def getActivityList() { + if (state.HarmonyAccessToken) { + def Params = [auth: state.HarmonyAccessToken] + def url = "https://home.myharmony.com/cloudapi/activity/all?${toQueryString(Params)}" + try { + httpGet(uri: url, headers: ["Accept": "application/json"]) {response -> + response.data.hubs.each { + def hub = getChildDevice("harmony-${it.key}") + if (hub) { + def hubname = getHubName("${it.key}") + def activities = [] + def aux = it.value.response.data.activities.size() + if (aux >= 1) { + activities = it.value.response.data.activities.collect { + [id: it.key, name: it.value['name'], type: it.value['type']] + } + activities += [id: "off", name: "Activity OFF", type: "0"] + } + hub.sendEvent(name: "activities", value: new groovy.json.JsonBuilder(activities).toString(), descriptionText: "Activities are ${activities.collect { it.name }?.join(', ')}", display: false) + } + } + } + } catch (groovyx.net.http.HttpResponseException e) { + log.trace e + } catch (java.net.SocketTimeoutException e) { + log.trace e + } catch(Exception e) { + log.trace e + } + } +} + def getActivityName(activity,hubId) { // GET ACTIVITY'S NAME def actname = activity @@ -610,7 +643,7 @@ def sendNotification(msg) { def hookEventHandler() { // log.debug "In hookEventHandler method." - log.debug "request = ${request}" + log.debug "Harmony - request = ${request}" def json = request.JSON @@ -619,14 +652,14 @@ def hookEventHandler() { } def listDevices() { - log.debug "getDevices, params: ${params}" + log.debug "Harmony - getDevices(), params: ${params}" allDevices.collect { deviceItem(it) } } def getDevice() { - log.debug "getDevice, params: ${params}" + log.debug "Harmony - getDevice(), params: ${params}" def device = allDevices.find { it.id == params.id } if (!device) { render status: 404, data: '{"msg": "Device not found"}' @@ -639,7 +672,7 @@ def updateDevice() { def data = request.JSON def command = data.command def arguments = data.arguments - log.debug "updateDevice, params: ${params}, request: ${data}" + log.debug "Harmony - updateDevice(), params: ${params}, request: ${data}" if (!command) { render status: 400, data: '{"msg": "command is required"}' } else { @@ -707,7 +740,7 @@ def getDeviceCapabilityCommands(deviceCapabilities) { } def listSubscriptions() { - log.debug "listSubscriptions()" + log.debug "Harmony - listSubscriptions()" app.subscriptions?.findAll { it.device?.device && it.device.id }?.collect { def deviceInfo = state[it.device.id] def response = [ @@ -728,17 +761,17 @@ def addSubscription() { def attribute = data.attributeName def callbackUrl = data.callbackUrl - log.debug "Logitech Harmony - addSubscription, params: ${params}, request: ${data}" + log.debug "Harmony - addSubscription, params: ${params}, request: ${data}" if (!attribute) { render status: 400, data: '{"msg": "attributeName is required"}' } else { def device = allDevices.find { it.id == data.deviceId } if (device) { if (!state.harmonyHubs) { - log.debug "Adding callbackUrl: $callbackUrl" + log.debug "Harmony - Adding callbackUrl: $callbackUrl" state[device.id] = [callbackUrl: callbackUrl] } - log.debug "Adding subscription" + log.debug "Harmony - Adding subscription" def subscription = subscribe(device, attribute, deviceHandler) if (!subscription || !subscription.eventSubscription) { subscription = app.subscriptions?.find { it.device?.device && it.device.id == data.deviceId && it.data == attribute && it.handler == 'deviceHandler' } @@ -766,7 +799,7 @@ def removeSubscription() { log.debug "removeSubscription, params: ${params}, subscription: ${subscription}, device: ${device}" if (device) { - log.debug "Removing subscription for device: ${device.id}" + log.debug "Harmony - Removing subscription for device: ${device.id}" state.remove(device.id) unsubscribe(device) } @@ -790,17 +823,17 @@ def deviceHandler(evt) { def deviceInfo = state[evt.deviceId] if (state.harmonyHubs) { state.harmonyHubs.each { harmonyHub -> - log.trace "Logitech Harmony - Sending data to $harmonyHub.name" + log.trace "Harmony - Sending data to $harmonyHub.name" sendToHarmony(evt, harmonyHub.callbackUrl) } } else if (deviceInfo) { if (deviceInfo.callbackUrl) { sendToHarmony(evt, deviceInfo.callbackUrl) } else { - log.warn "No callbackUrl set for device: ${evt.deviceId}" + log.warn "Harmony - No callbackUrl set for device: ${evt.deviceId}" } } else { - log.warn "No subscribed device found for device: ${evt.deviceId}" + log.warn "Harmony - No subscribed device found for device: ${evt.deviceId}" } } @@ -824,12 +857,12 @@ def sendToHarmony(evt, String callbackUrl) { body: [evt: [deviceId: evt.deviceId, name: evt.name, value: evt.value]] ] try { - log.debug "Sending data to Harmony Cloud: $params" + log.debug "Harmony - Sending data to Harmony Cloud: $params" httpPostJson(params) { resp -> - log.debug "Harmony Cloud - Response: ${resp.status}" + log.debug "Harmony - Cloud Response: ${resp.status}" } } catch (e) { - log.error "Harmony Cloud - Something went wrong: $e" + log.error "Harmony - Cloud Something went wrong: $e" } } } @@ -854,10 +887,10 @@ def activityCallback() { if (data.errorCode == "200") { device.setCurrentActivity(data.currentActivityId) } else { - log.warn "Activity callback error: ${data}" + log.warn "Harmony - Activity callback error: ${data}" } } else { - log.warn "Activity callback sent to non-existant dni: ${params.dni}" + log.warn "Harmony - Activity callback sent to non-existant dni: ${params.dni}" } render status: 200, data: '{"msg": "Successfully received callbackUrl"}' } @@ -891,13 +924,13 @@ def harmony() { } def deleteHarmony() { - log.debug "Trying to delete Harmony hub with mac: ${params.mac}" + log.debug "Harmony - Trying to delete Harmony hub with mac: ${params.mac}" def harmonyHub = state.harmonyHubs?.find { it.mac == params.mac } if (harmonyHub) { - log.debug "Deleting Harmony hub with mac: ${params.mac}" + log.debug "Harmony - Deleting Harmony hub with mac: ${params.mac}" state.harmonyHubs.remove(harmonyHub) } else { - log.debug "Couldn't find Harmony hub with mac: ${params.mac}" + log.debug "Harmony - Couldn't find Harmony hub with mac: ${params.mac}" } render status: 204, data: "{}" }