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 15a83f6..d7db5bd 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,24 +83,105 @@ def parse(String description) { } } else { - def cluster = zigbee.parse(description) + Map bindingTable = parseBindingTableResponse(description) + if (bindingTable) { + List cmds = [] + bindingTable.table_entries.inject(cmds) { acc, entry -> + // The binding entry is not for our hub and should be deleted + if (entry["dstAddr"] != zigbeeEui) { + acc.addAll(removeBinding(entry.clusterId, entry.srcAddr, entry.srcEndpoint, entry.dstAddr, entry.dstEndpoint)) + } + acc + } + // There are more entries that we haven't examined yet + if (bindingTable.numTableEntries > bindingTable.startIndex + bindingTable.numEntriesReturned) { + def startPos + if (cmds) { + log.warn "Removing binding entries for other devices: $cmds" + // Since we are removing some entries, we should start in the same spot as we just read since values + // will fill in the newly vacated spots + startPos = bindingTable.startIndex + } else { + // Since we aren't removing anything we move forward to the next set of table entries + startPos = bindingTable.startIndex + bindingTable.numEntriesReturned + } + cmds.addAll(requestBindingTable(startPos)) + } + sendHubCommand(cmds.collect { it -> + new physicalgraph.device.HubAction(it) + }, 2000) + } else { + 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]) + 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 "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}" + log.warn "DID NOT PARSE MESSAGE for description : $description" + log.debug "${cluster}" } } - else { - log.warn "DID NOT PARSE MESSAGE for description : $description" - log.debug "${cluster}" - } } } +def parseBindingTableResponse(description) { + Map descMap = zigbee.parseDescriptionAsMap(description) + if (descMap["clusterInt"] == 0x8033) { + def header_field_lengths = ["transactionSeqNo": 1, "status": 1, "numTableEntries": 1, "startIndex": 1, "numEntriesReturned": 1] + def field_values = [:] + def data = descMap["data"] + header_field_lengths.each { k, v -> + field_values[k] = Integer.parseInt(data.take(v).join(""), 16); + data = data.drop(v); + } + + List table = [] + if (field_values.numEntriesReturned) { + def table_entry_lengths = ["srcAddr": 8, "srcEndpoint": 1, "clusterId": 2, "dstAddrMode": 1] + for (def i : 0..(field_values.numEntriesReturned - 1)) { + def entryMap = [:] + table_entry_lengths.each { k, v -> + def val = data.take(v).reverse().join("") + entryMap[k] = val.length() < 8 ? Integer.parseInt(val, 16) : val + data = data.drop(v) + } + + switch (entryMap.dstAddrMode) { + case 0x01: + entryMap["dstAddr"] = data.take(2).reverse().join("") + data = data.drop(2) + break + case 0x03: + entryMap["dstAddr"] = data.take(8).reverse().join("") + data = data.drop(8) + entryMap["dstEndpoint"] = Integer.parseInt(data.take(1).join(""), 16) + data = data.drop(1) + break + } + table << entryMap + } + } + field_values["table_entries"] = table + return field_values + } + return [:] +} + +def requestBindingTable(startPos=0) { + return ["zdo mgmt-bind 0x${zigbee.deviceNetworkId} $startPos"] +} + +def removeBinding(cluster, srcAddr, srcEndpoint, destAddr, destEndpoint) { + return ["zdo unbind unicast 0x${zigbee.deviceNetworkId} {${srcAddr}} $srcEndpoint $cluster {${destAddr}} $destEndpoint"] +} + + def off() { zigbee.off() } @@ -130,8 +211,7 @@ def configure() { // enrolls with default periodic reporting until newer 5 min interval is confirmed sendEvent(name: "checkInterval", value: 3 * 10 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) - // OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity - refresh() + refresh() + requestBindingTable(0) + ["delay 2000"] } def setColorTemperature(value) {