Update to use multi attr parsing in zigbee library

With the addition of the ability for parseDescriptionAsMap to hanle the
return of multiple read attribute responses, we can be a little more
explicit with how we interpret and parse the responses.
This commit is contained in:
Zach Varberg
2016-12-07 09:42:28 -06:00
parent 65d4a811b0
commit 1e02387387

View File

@@ -117,14 +117,16 @@ metadata {
} }
def parse(String description) { def parse(String description) {
Map map = zigbee.getEvent(description) def maps = []
if (!map) { maps << zigbee.getEvent(description)
if (!maps[0]) {
maps = []
if (description?.startsWith('zone status')) { if (description?.startsWith('zone status')) {
map = parseIasMessage(description) maps += parseIasMessage(description)
} else { } else {
Map descMap = zigbee.parseDescriptionAsMap(description) Map descMap = zigbee.parseDescriptionAsMap(description)
if (descMap?.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) { if (descMap?.clusterInt == 0x0001 && descMap.commandInt != 0x07 && descMap?.value) {
map = getBatteryResult(Integer.parseInt(descMap.value, 16)) maps << getBatteryResult(Integer.parseInt(descMap.value, 16))
} else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) { } else if (descMap?.clusterInt == zigbee.TEMPERATURE_MEASUREMENT_CLUSTER && descMap.commandInt == 0x07) {
if (descMap.data[0] == "00") { if (descMap.data[0] == "00") {
log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap" log.debug "TEMP REPORTING CONFIG RESPONSE: $descMap"
@@ -133,10 +135,12 @@ def parse(String description) {
log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}" log.warn "TEMP REPORTING CONFIG FAILED- error code: ${descMap.data[0]}"
} }
} else { } else {
map = handleAcceleration(descMap)
maps += handleAcceleration(descMap)
} }
} }
} else if (map.name == "temperature") { } else if (maps[0].name == "temperature") {
def map = maps[0]
if (tempOffset) { if (tempOffset) {
map.value = (int) map.value + (int) tempOffset map.value = (int) map.value + (int) tempOffset
} }
@@ -144,8 +148,11 @@ def parse(String description) {
map.translatable = true map.translatable = true
} }
def result = map ? createEvent(map) : [:] def result = maps.inject([]) {acc, it ->
if (it) {
acc << createEvent(it)
}
}
if (description?.startsWith('enroll request')) { if (description?.startsWith('enroll request')) {
List cmds = zigbee.enrollResponse() List cmds = zigbee.enrollResponse()
log.debug "enroll response: ${cmds}" log.debug "enroll response: ${cmds}"
@@ -154,59 +161,81 @@ def parse(String description) {
return result return result
} }
private Map handleAcceleration(descMap) { private List<Map> handleAcceleration(descMap) {
Map result = [:] def result = []
if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0010) { if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0010) {
if (descMap.value.size() == 32) { def value = descMap.value == "01" ? "active" : "inactive"
// value will look like 00ae29001403e2290013001629001201 log.debug "Acceleration $value"
// breaking this apart and swapping byte order where appropriate, this breaks down to: result << [
// X (0x0012) = 0x0016 name : "acceleration",
// Y (0x0013) = 0x03E2 value : value,
// Z (0x0014) = 0x00AE descriptionText: "{{ device.displayName }} was $value",
// note that there is a known bug in that the x,y,z attributes are interpreted in the wrong order isStateChange : isStateChange(device, "acceleration", value),
// this will be fixed in a future update translatable : true
def threeAxisAttributes = descMap.value[0..-9] ]
result << parseAxis(threeAxisAttributes)
descMap.value = descMap.value[-2..-1] if (descMap.additionalAttrs) {
result += parseAxis(descMap.additionalAttrs)
} }
result = getAccelerationResult(descMap.value) } else if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0012) {
} else if (descMap.clusterInt == 0xFC02 && descMap.attrInt == 0x0012 && descMap.value.size() == 24) { def addAttrs = descMap.additionalAttrs
// The size is checked to ensure the attribute report contains X, Y and Z values addAttrs << ["attrInt": descMap.attrInt, "value": descMap.value]
// If all three axis are not included then the attribute report is ignored result += parseAxis(addAttrs)
result = parseAxis(descMap.value)
} }
return result return result
} }
private Map parseIasMessage(String description) { private List<Map> parseAxis(List<Map> attrData) {
ZoneStatus zs = zigbee.parseZoneStatus(description) def results = []
Map resultMap = [:] def x = hexToSignedInt(attrData.find { it.attrInt == 0x0012 }?.value)
def y = hexToSignedInt(attrData.find { it.attrInt == 0x0013 }?.value)
def z = hexToSignedInt(attrData.find { it.attrInt == 0x0014 }?.value)
if (garageSensor != "Yes") { def xyzResults = [:]
resultMap = zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed') if (device.getDataValue("manufacturer") == "SmartThings") {
// This mapping matches the current behavior of the Device Handler for the Centralite sensors
xyzResults.x = z
xyzResults.y = y
xyzResults.z = -x
} else {
// The axises reported by the Device Handler differ from the axises reported by the sensor
// This may change in the future
xyzResults.x = z
xyzResults.y = x
xyzResults.z = y
} }
return resultMap log.debug "parseAxis -- ${xyzResults}"
if (garageSensor == "Yes")
results += garageEvent(xyzResults.z)
def value = "${xyzResults.x},${xyzResults.y},${xyzResults.z}"
results << [
name : "threeAxis",
value : value,
linkText : getLinkText(device),
descriptionText: "${getLinkText(device)} was ${value}",
handlerName : name,
isStateChange : isStateChange(device, "threeAxis", value),
displayed : false
]
results
} }
def updated() { private List<Map> parseIasMessage(String description) {
log.debug "updated called" ZoneStatus zs = zigbee.parseZoneStatus(description)
log.info "garage value : $garageSensor" List<Map> results = []
if (garageSensor == "Yes") {
def descriptionText = "Updating device to garage sensor" if (garageSensor != "Yes") {
if (device.latestValue("status") == "open") { def value = zs.isAlarm1Set() ? 'open' : 'closed'
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true) log.debug "Contact: ${device.displayName} value = ${value}"
} else if (device.latestValue("status") == "closed") { def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true) results << [name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true]
} results << [name: 'status', value: value, descriptionText: descriptionText, translatable: true]
} else {
def descriptionText = "Updating device to open/close sensor"
if (device.latestValue("status") == "garage-open") {
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true)
} else if (device.latestValue("status") == "garage-closed") {
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true)
}
} }
return results
} }
private Map getBatteryResult(rawValue) { private Map getBatteryResult(rawValue) {
@@ -247,35 +276,24 @@ private Map getBatteryResult(rawValue) {
return result return result
} }
private Map getContactResult(value) { List<Map> garageEvent(zValue) {
log.debug "Contact: ${device.displayName} value = ${value}" List<Map> results = []
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed' def absValue = zValue.abs()
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true) def contactValue = null
return [name: 'status', value: value, descriptionText: descriptionText, translatable: true] def garageValue = null
} if (absValue > 900) {
contactValue = 'closed'
private getAccelerationResult(numValue) { garageValue = 'garage-closed'
log.debug "Acceleration" } else if (absValue < 100) {
def name = "acceleration" contactValue = 'open'
def value garageValue = 'garage-open'
def descriptionText
if (numValue.endsWith("1")) {
value = "active"
descriptionText = '{{ device.displayName }} was active'
} else {
value = "inactive"
descriptionText = '{{ device.displayName }} was inactive'
} }
if (contactValue != null) {
def isStateChange = isStateChange(device, name, value) def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
return [ results << [name: 'contact', value: contactValue, descriptionText: descriptionText, displayed: false, translatable: true]
name : name, results << [name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true]
value : value, }
descriptionText: descriptionText, results
isStateChange : isStateChange,
translatable : true
]
} }
/** /**
@@ -332,35 +350,24 @@ def configure() {
return refresh() + configCmds return refresh() + configCmds
} }
private getEndpointId() { def updated() {
new BigInteger(device.endpointId, 16).toString() log.debug "updated called"
} log.info "garage value : $garageSensor"
if (garageSensor == "Yes") {
private Map parseAxis(String description) { def descriptionText = "Updating device to garage sensor"
def z = hexToSignedInt(description[0..3]) if (device.latestValue("status") == "open") {
def y = hexToSignedInt(description[10..13]) sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true)
def x = hexToSignedInt(description[20..23]) } else if (device.latestValue("status") == "closed") {
def xyzResults = [x: x, y: y, z: z] sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true)
}
if (device.getDataValue("manufacturer") == "SmartThings") {
// This mapping matches the current behavior of the Device Handler for the Centralite sensors
xyzResults.x = z
xyzResults.y = y
xyzResults.z = -x
} else { } else {
// The axises reported by the Device Handler differ from the axises reported by the sensor def descriptionText = "Updating device to open/close sensor"
// This may change in the future if (device.latestValue("status") == "garage-open") {
xyzResults.x = z sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true)
xyzResults.y = x } else if (device.latestValue("status") == "garage-closed") {
xyzResults.z = y sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true)
}
} }
log.debug "parseAxis -- ${xyzResults}"
if (garageSensor == "Yes")
garageEvent(xyzResults.z)
getXyzResult(xyzResults, description)
} }
private hexToSignedInt(hexVal) { private hexToSignedInt(hexVal) {
@@ -368,43 +375,6 @@ private hexToSignedInt(hexVal) {
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
} }
def garageEvent(zValue) {
def absValue = zValue.abs()
def contactValue = null
def garageValue = null
if (absValue > 900) {
contactValue = 'closed'
garageValue = 'garage-closed'
} else if (absValue < 100) {
contactValue = 'open'
garageValue = 'garage-open'
}
if (contactValue != null) {
def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed: false, translatable: true)
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true)
}
}
private Map getXyzResult(results, description) {
def name = "threeAxis"
def value = "${results.x},${results.y},${results.z}"
def linkText = getLinkText(device)
def descriptionText = "$linkText was $value"
def isStateChange = isStateChange(device, name, value)
[
name : name,
value : value,
unit : null,
linkText : linkText,
descriptionText: descriptionText,
handlerName : name,
isStateChange : isStateChange,
displayed : false
]
}
private getManufacturerCode() { private getManufacturerCode() {
if (device.getDataValue("manufacturer") == "SmartThings") { if (device.getDataValue("manufacturer") == "SmartThings") {
return "0x110A" return "0x110A"