Writing Matter Attributes - What Am I doing wrong?

@kkossev @bcopeland

I could use some help on what should be a simple task

I've been trying to do a simple test of writing to a device attribute. Here's the code:

void testSettingTransitionTime(){
         List<Map<String, String>> attrWriteRequests = []
            attrWriteRequests.add(matter.attributeWriteRequest(0x01, 0x0008, 0x0010, DataType.UINT16, "4000" )) // Set to hex 0x40 = 48
            String cmd = matter.writeAttributes(attrWriteRequests)
        log.debug "Writing: ${attrWriteRequests} using command string: ${cmd}"
         sendHubCommand(new hubitat.device.HubAction(cmd, hubitat.device.Protocol.MATTER))
}

This writes a value of 0x40 to the OnOffTransitionTime attribute of the Level cluster. I've similarly also tried writes to other attributes like OnLevel, and to the StartUpOnOff attribute 0x4003 of the On/Off cluster. I've also tried to Tapo WiFi dimmers, AiDot bulbs, and Nanoleaf devices with no luck - no matter what I try to write to, the attributes remain unchanged

I figure I'm doing something wrong with the write command. Does anybody have examples of a write command that they have tested and know works (and which devices did they work on), or know why my code isn't working?

Here's an example of the logged command produced by my example code. All looks OK

dev:522024-03-10 11:27:33.107 AMdebugWriting: [[ep:0x01, cluster:0x0008, attr:0x0010, data:050040]] using command string: he wattrs [{"ep":"0x01","cluster":"0x0008","attr":"0x0010","data":"050040"}]

Hmmm, is it an issue of privileges (Section 7.6 of Matter Core Spec.)?

Per the Matter Spec ...
StartUpOnOff attribute of the on/off cluster requires "VM" privilege (View and Manage)
OnLevel, OnTransitionTime, OffTransitionTime of the Level cluster all require View and Operate privileges

What privilege does a Hubitat Driver have?

1 Like

Actually, I have not made it work as well. Some months ago, I tried writeAttribute to Identify cluster without success. And I have left this issue 'for later' ...

Also, writing to a Matter Lock cluster attribute 0x0000 (LockState) failed in my attempts to control a lock.

The "LockState" issue you are having is a bit more straightforward (I think). LockState is a Read-Only Attribute. See Access column in Section 5.2.6 of the Matter Cluster Spec:

What you need to do is to send one of the Commands from Section 5.2.7 of the Matter Cluster Spec. to cause a Lock or Unlock to occur. That will then cause the lock to change the LockState attribute. In other words, if a Attribute is a "R" rather than "RW" only the device itself can change it (usually following some event or command).

The commands for Lock and Unlock should be formed very similar to "On" and "Off" in my cluster library for cluster 0x0006. I did some quick edits to those (untested) - but you could give these a try (they take an endpoint parameter named "ep:") - for example

lock(ep:1)
// lock implements Matter 1.2 Cluster Spec Section 5.2.7 lock command
void lock( Map params = [:] ){
    try { 
        Map inputs = [ ep: null ] << params
        assert inputs.ep instanceof Integer  // Use Integer, not Hex! 
        sendHubCommand(new hubitat.device.HubAction(matter.invoke(inputs.ep, 0x0101, 0x00), hubitat.device.Protocol.MATTER))
    } catch (AssertionError e) {
        log.error "Incorrect parameter type or value used in lock() method.<br><pre>${e}<br><br>Stack trace:<br>${getStackTrace(e) }"
    } catch(e){
        log.error "<pre>${e}<br><br>when processing description string ${description}<br><br>Stack trace:<br>${getStackTrace(e) }"
    }   
}

// unlock implements Matter 1.2 Cluster Spec Section 5.2.7 unlock command

void unlock( Map params = [:] ){
    try { 
        Map inputs = [ ep: null ] << params
        assert inputs.ep instanceof Integer // Use Integer, not Hex!

        sendHubCommand(new hubitat.device.HubAction(matter.invoke(inputs.ep, 0x0101, 0x01 ), hubitat.device.Protocol.MATTER))  
    } catch (AssertionError e) {
        log.error "Incorrect parameter type or value used in unlock() method.<br><pre>${e}<br><br>Stack trace:<br>${getStackTrace(e) }"
    } catch(e){
        log.error "<pre>${e}<br><br>when processing description string ${description}<br><br>Stack trace:<br>${getStackTrace(e) }"
    }     
}
1 Like

Yep, I was confused with LockState description in 5.2.6.1. Scene Table Extensions, where the attribute is treated as a writable command.

Anyway, the LockDoor and UnlckDoor commands did not work during my tests. They result in Hubitat calling the initialize() method on the next command.

This is the simple code that I have tried last time :

    String cmd = matter.invoke(deviceNumber, 0x0101, 0x00) // 0x0101 = DoorLock Cluster, 0x00 = LockDoor
    logDebug "componentLock(): sending command '${cmd}'"
    sendToDevice(cmd)

Logs:

dev:45892024-03-10 21:49:10.687debugNuki Lock sendToDevice (String): (he invoke 0x01 0x0101 0x0000 {1518})
dev:45892024-03-10 21:49:10.683debugNuki Lock componentLock(): sending command 'he invoke 0x01 0x0101 0x0000 {1518}'
dev:45892024-03-10 21:49:10.670infoNuki Lock sending Lock command to device# 1 (01) Bridge#4589 Device#01

And the result ...

dev:45892024-03-10 21:50:53.915warninitialize()...

The Matter connection to the Nuki lock is OK because I receive successfully the LockState attribute updates (locked/unlocked) when the lock is controlled locally or from other Matter controllers (Apple Home, for example).

(and I just noticed - I have to fix a bunch of my error messages)

Have you had any luck writing a Matter attribute?

If so, can you show example code?

I did a very simple driver that writes to the OnLevel attribute 0x0011 for Matter Cluster 0x0008 and then reads it back (with a Refresh). This is a mandatory RW cluster.

It looks like the write is sent properly, but when I read back, there is no change.

It should work on any dimmer / bulb and I've tested with Nanoleaf to no effect.

import hubitat.matter.DataType
import hubitat.helper.HexUtils

metadata {
    definition (name: "Matter Simple Write Test Driver", namespace: "matterTools", author: "jvm33") {
		capability "Refresh"
    }
    
    preferences {
        input( name: "MatterOnLevel", type:"number", title:"Turn On Level (%)", description: "When turning on light, set to this level.", range:"1..100")   
    }
}


void parse(String description) {
    log.debug matter.parseDescriptionAsMap(description)
}

void updated(){
    log.info "${device.displayName}: Processing Preference changes..."
    Integer MatterOnLevel = device.getSetting("MatterOnLevel") * 2.54

    if (MatterOnLevel) {
        String MatterOnLevelHex = HexUtils.integerToHexString(MatterOnLevel, 1)
        List<Map<String, String>> attrWriteRequests = []
        attrWriteRequests.add(matter.attributeWriteRequest(1, 0x0008, 0x0011, DataType.UINT8, MatterOnLevelHex))
        String cmd = matter.writeAttributes(attrWriteRequests)
        log.debug "Setting Matter onLevel to ${MatterOnLevel} using command : ${cmd}"
        sendHubCommand(new hubitat.device.HubAction(cmd, hubitat.device.Protocol.MATTER))
    
    }
}

void refresh() {
    refreshMatter(ep:0x01, clusterInt: 0x0008, attrInt: 0x0011) 
}
    

    // Performs a refresh on a designated endpoint / cluster / attribute (all specified in Integer)
// Does a wildcard refresh if parameters are not specified (ep=FFFF / cluster=FFFFFFFF/ endpoint=FFFFFFFF is the Matter wildcard designation
void refreshMatter(Map params = [:]) {
    try {
        Map inputs = [ep:0xFFFF, clusterInt: 0xFFFFFFFF, attrInt: 0xFFFFFFFF] << params
        assert inputs.ep instanceof Integer         // Make sure the type is as expected! 
        assert inputs.clusterInt instanceof Integer || inputs.clusterInt instanceof Long
        assert inputs.attrInt instanceof Integer || inputs.attrInt instanceof Long
        
       // Groovy Slashy String form of a GString  https://docs.groovy-lang.org/latest/html/documentation/#_slashy_string
        String cmd = /he rattrs [{"ep":"${inputs.ep}","cluster":"${inputs.clusterInt}","attr":"${inputs.attrInt}"}]/

        sendHubCommand(new hubitat.device.HubAction(cmd, hubitat.device.Protocol.MATTER))
    } catch (AssertionError e) {
        log.error "<pre>${e}<br><br>Stack trace:<br>${getStackTrace(e) }"
    } catch(e){
        log.error "<pre>${e}<br><br>when processing refreshMatter with inputs ${inputs}<br><br>Stack trace:<br>${getStackTrace(e) }"
    }   
}

Hi @jvm33 ,

No, currently I am not using any writeAttributes Matter commands in any of my drivers at this time.
I temporarily put my custom Matter driver developments on hold until there are some improvements in the Hubitat platform related to error processing.

HE invoking automatically the initialize() method on each and every error returned by the matter device makes the developments really difficult.