I'm new to Groovy and working on my first driver (zigbee). The full driver is included at the bottom of this topic.
My problem is in the configureReporting method. Here is the snippet with the issue. The first log entry is as I expect. The second entry is just [].
List attrs =
[electricalMeasurementCluster.attrs.rmsVoltage,
electricalMeasurementCluster.attrs.rmsCurrent,
electricalMeasurementCluster.attrs.activePower]
log.debug "commands: ${commands}" // correct- logs shows [configureReporting:06, readReportingConfiguration:08, reportAttributes:0A, defaultResponse:0B, discoverAttributesExtended:15]
attrs.each() {
log.debug "commands: ${commands}" // bad- logs show []
commands is defined as:
@Field static Map<String,String> commands = [
configureReporting: "06",
readReportingConfiguration:"08",
reportAttributes:"0A",
defaultResponse:"0B",
discoverAttributesExtended: "15"]
Here is the complete driver.
/*
Switch Driver for Third Reality 3RSP02028BZ
Comments reference sections of Zigbee Alliance Cluster Library Specification,
Document 07-5123 Revision 8, 6 Chapter Document 14-0125
https://zigbeealliance.org/wp-content/uploads/2021/10/07-5123-08-Zigbee-Cluster-Library.pdf
References to the spec will be abbreviated at ZS + a section number.
2023 1.0
*/
/* Libraries from https://github.com/jvmahon/Hubitat-Zigbee.
See also https://community.hubitat.com/t/zigbee-full-format-for-sending-commands-using-he-raw/101926 */
#include zigbeeTools.sendZigbeeAdvancedCommands
#include zigbeeTools.endpointAndChildDeviceTools
import hubitat.helper.HexUtils
/* See ZS 4.9 */
@Field static Map electricalMeasurementCluster = [id: "0B04",
attrs: [activePower: "050B", rmsCurrent: "0508", rmsVoltage: "0505"]]
/* See ZS 3.8 */
@Field static Map onOffCluster = [id: "0006",
attrs: [onOff: "0000"]]
/* See ZS 2.6.2.2 */
@Field static Map<String,String> dataTypes = [
int16: "29",
uint16: "21",
bool: "10"
]
/* See ZS 2.5.9.1.2 */
@Field static Map<String,String> direction = [
reported: "00",
received: "01"
]
/* See ZB 2.5 */
@Field static Map<String,String> commands = [
configureReporting: "06",
readReportingConfiguration:"08",
reportAttributes:"0A",
defaultResponse:"0B",
discoverAttributesExtended: "15"]
/* See https://docs2.hubitat.com/developer/driver/definition and
https://stdavedemo.readthedocs.io/en/latest/index.html */
metadata {
definition(name: "Paige Power Switch", namespace: "hubitat", author: "Paige Vinall", component: true) {
capability "Switch"
capability "Refresh"
capability "PowerMeter"
capability "Actuator"
capability "Configuration"
attribute "LastUpdate", "number" // Time stamp of last message from device
attribute "RMSCurrent", "number"
attribute "RMSVoltage", "number"
command "logDeviceInfo"
fingerprint profileId:"0104", endpointId:"01", inClusters:"0000,0003,0004,0005,0006,1000,0B04", outClusters:"0019", model:"3RSP02028BZ", manufacturer:"Third Reality, Inc"
}
preferences {
input name: "logEnable", type: "bool", title: "Enable logging", defaultValue: true
}
}
Long convertSignedHexToInt(String hex) {
Long.parseLong(hex, 16);
}
Long decodeNumericValue(String value, String encoding) {
if (encoding==dataTypes.int16) return convertSignedHexToInt(value)
else if (encoding==dataTypes.uint16) return HexUtils.hexStringToInt(value)
else {
log.debug "Unknown encoding dataType ${encoding} in decodeNumericValue"
return 0
}
}
void updated() {
if (logEnable) log.info "Updated..."
log.warn "description logging is: ${txtEnable == true}"
}
void installed() {
if (logEnable) log.info "Installed..."
device.updateSetting("txtEnable",[type:"bool",value:true])
refresh()
}
void setLastUpdate() {
def name = "lastUpdate"
Date date = new Date();
def valueText = date.toString()
def descriptionText = "${name} is ${valueText}"
def value = date.getTime()
if (logEnable) log.warn descriptionText
sendEvent(name:name,value:value,descriptionText:descriptionText,unit:"ms")
}
/* Logs information about device. Not currently used. */
void logDeviceInfo() {
log.warn "device.getData(): "+device.getData()
log.warn "capabilities: "+device.capabilities
log.warn "attributes: "+device.attributes
log.warn "supportedAttributes: "+device.supportedAttributes
}
/* See ZS 2.5.2 */
void processReportAttributeReadRecord(cluster,attrId,encoding,value) {
int numericValue = decodeNumericValue(value,encoding)
boolean boolValue = (value=="01")
if (logEnable) log.warn "cluster ${cluster}, attrId ${attrId}, encoding: ${encoding}"
if (cluster==electricalMeasurementCluster.id && attrId==electricalMeasurementCluster.attrs.activePower) {
sendEvent(name:"power",value:numericValue,descriptionText:"power is ${numericValue}", unit:"W")
}
else if (cluster==electricalMeasurementCluster.id && attrId==electricalMeasurementCluster.attrs.rmsCurrent) {
sendEvent(name:"RMSCurrent",value:numericValue,descriptionText:"RMScurrent is ${numericValue}", unit:"A")
}
else if (cluster==electricalMeasurementCluster.id && attrId==electricalMeasurementCluster.attrs.rmsVoltage) {
sendEvent(name:"RMSVoltage",value:numericValue,descriptionText:"${numericValue}", unit:"V")
}
else if (cluster==onOffCluster.id && attrId==onOffCluster.attrs.onOff) {
if (boolValue)
sendEvent(name:"switch",value:"on",descriptionText:"outlet is on")
else
sendEvent(name:"switch",value:"off",descriptionText:"outlet is off")
}
else {
log.debug "Unexpected report attribute: cluster ${cluster}, attrId ${attrId}, encoding: ${encoding}, value: ${value}"
}
}
/* See ZS 2.5.2 */
/* Makes a list of all attribute read records and then iterates through them.
Note that my devices only return a single record so some of this code is untested. */
void processReportAttributes(Map msgMap) {
List<Map> attrList = []
attrList.add(["attrId":msgMap.attrId, "value":msgMap.value, "encoding":msgMap.encoding])
if (msgMap.additionalAttrs) attrList.addAll(msgMap.additionalAttrs) // Untested
if (logEnable) log.warn "additionalAttr: ${attrList.inspect()}"
attrList.each{
processReportAttributeReadRecord(msgMap.cluster,it.attrId,it.encoding,it.value)
}
}
void parse(String description) {
setLastUpdate()
def msgMap = zigbee.parseDescriptionAsMap(description)
if (logEnable) log.warn "description: ${description}"
if (logEnable) log.warn "msgMap: ${msgMap.inspect()}"
switch (msgMap.command){
case commands.reportAttributes: //Report attributes
processReportAttributes(msgMap)
break
case commands.defaultResponse: //Default Response
break
default:
log.debug "Unexpected response in parse: ${msgMap.inspect()}"
}
}
/* Never gets called */
void parse(List description) {
log.debug "Parse(List) called unexpectedly with ${List.inspect()}"
description.each {
log.warn "It=${it}"
if (it.name in ["switch"]) {
if (txtEnable) log.info it.descriptionText
sendEvent(it)
}
}
}
/* Called when hubitat wants switch turned on. See https://docs2.hubitat.com/developer/zigbee-object */
def on() {
if (logEnable) log.warn "on cmd: "+zigbee.on()
return zigbee.on()
}
/* Called when hubitat wants switch turned off. See https://docs2.hubitat.com/developer/zigbee-object */
def off() {
if (logEnable) log.warn "off cmd: "+zigbee.off()
return zigbee.off()
}
/* See ZS 2.5.22 */
/* Not called by driver but used in creating driver. */
void discoverAttributesExtended() {
String startAttributeIdentifier="0000"
String maximumAttributeIdentifiers="FF"
String payload = startAttributeIdentifier+maximumAttributeIdentifiers
sendZCLAdvanced(
destinationNetworkId: device.deviceNetworkId,
destinationEndpoint: 1 ,
sourceEndpoint: 1,
clusterId: electricalMeasurementCluster.id,
profileId: 0x0000,
commandId: commands.discoverAttributesExtended,
commandPayload: payload,
commandPayloadAutoreverse: false
)
}
/* See ZS 2.5.7 */
void configureReporting() {
String minimumReportingInterval="0100"
String maximumReportingInterval="FEFF"
String reportableChangeField="0001"
String payload
List attrs =
[electricalMeasurementCluster.attrs.rmsVoltage,
electricalMeasurementCluster.attrs.rmsCurrent,
electricalMeasurementCluster.attrs.activePower]
log.debug "commands: ${commands}" // correct- logs [configureReporting:06, readReportingConfiguration:08, reportAttributes:0A, defaultResponse:0B, discoverAttributesExtended:15]
attrs.each() {
log.debug "commands: ${commands}" // bad- logs []
payload = direction.reported+
it+
dataTypes.uint16+
minimumReportingInterval+
maximumReportingInterval+
reportableChangeField
if (logEnable) log.warn "payload: ${payload}"
sendZCLAdvanced(
destinationNetworkId: device.deviceNetworkId,
destinationEndpoint: 1 ,
sourceEndpoint: 1,
clusterId: electricalMeasurementCluster.id,
profileId: 0x0000,
commandId: commands.configureReporting,
commandPayload: payload,
commandPayloadAutoreverse: false
)
}
}
/* See ZS 2.5.9.1 */
/* Not currently used by driver. Used during development. */
void readReportingConfiguration() {
String payload = direction.reported+electricalMeasurementCluster.attrs.activePower
//+direction.reported+electricalMeasurementCluster.attrs.rmsCurrent
if (logEnable) log.warn "payload: ${payload}"
sendZCLAdvanced(
destinationNetworkId: device.deviceNetworkId,
destinationEndpoint: 1 ,
sourceEndpoint: 1,
clusterId: electricalMeasurementCluster.id,
profileId: 0x0000,
commandId: commands.readReportingConfiguration,
commandPayload: payload,
commandPayloadAutoreverse: false,
)
payload = direction.reported+onOffCluster.attrs.onOff
if (logEnable) log.warn "payload: ${payload}"
sendZCLAdvanced(
destinationNetworkId: device.deviceNetworkId,
destinationEndpoint: 1 ,
sourceEndpoint: 1,
clusterId: onOffCluster.id,
profileId: 0x0000,
commandId: commands.readReportingConfiguration,
commandPayload: payload,
commandPayloadAutoreverse: false,
)
}
void refresh() {
readReportingConfiguration()
}
void configure() {
configureReporting()
}