Zigbee Description parse error

When parsing this description:

read attr - raw: A5BA0100003002FF4C06001001211E0C21A813240C000000002163002054, dni: A5BA, endpoint: 01, cluster: 0000, size: 30, attrId: FF02, encoding: 4C, command: 0A, value: 06001001211E0C21A813240C000000002163002054

with this command in the parse method:
Map msgMap = zigbee.parseDescriptionAsMap(description)

It produces this error:
java.lang.StringIndexOutOfBoundsException: String index out of range: -2 on line 155 (parse)

Just to be clear, this is the only description which doesn't parse properly. What I don't know yet is if the message is corrupt/incorrect or the parser interprets it wrong and that is the cause? This is a status event sent once per hour by the device, it errors out EVERY time. Only on this message.

@mike.maxwell if the description message is corrupt/incorrect I'll work around it in my code, if it is the parser that is wrong, I'll patch it for now but wanted to report it as a bug.

EDIT: Is this because the encoding type is a STRUCT (4C)? STRING16 (44) works.

My bet is the length being advertised for the string is off by one, ie its too long...

Edit, sorry I got this backwards, it's comming in as 4C.

Off the top of my head I don't recall how this data type works, so I can't say where the problem is, I will have a look though and get back to you.

1 Like

Really? Just changing encoding from 4C to 44 works. Since I'm no expert in parsing Zigbee packets I would like to ask what the expected size value should be with 21 bytes (42 characters)? Shouldn't that be 48 (0x30)?

Ok, thank you!

The problem is the parser... The data is correct... The value field is a structure and decodes as:

value = 06001001211E0C21A813240C000000002163002054

element 0 = 06 00 = (6 elements in structure)
element 1 = 10 01 = (data type boolean) true
element 2 = 21 1E 0C = (uint16)0x1E0C
element 3 = 21 A8 13 = ((uint16) 0xA813
element 4 = 24 0C 00 00 00 00 = (uint40) 0x0C00000000
element 5 = 21 63 00 = (uint16) 0x6300
element 6 = 20 54 = (uint8) 0x54

Except that it really doesn't... Type 44 is a string... a single discrete value, where the structure is multiple (different) discrete values...

The attribute ID is FF02.... Which indicated that this is an custom attribute for an Xiaomi device... which encodes on/off state (element 1) and battery percentage (element 2)

2 Likes

Where would I find good documentation for this? Looking at what you wrote out in your post I fully understand how to parse it, thank you!

I know, more of a way to say, the data and sizes ought to be correct if it is just a matter of parsing the data as something else. Technically very much incorrect and useless to do, but still.

Yes, it's a Xiaomi device. So not anything unknown. Just writing this for my own understanding.

The zigbee cluster library specification documents HERE

Specifically, section 2.6.2 (Data Types) and section 2.6.2.16 (Structure)

1 Like

This is some awesome information. Jewels like this I bookmark for future reference.

Just a question, how did you know what element 1 and 2 were for. Was that just from knowledge?

I always wanted to try to understand how to break down that message but it makes a lot of sense now that you explained it with the document reference.

Thank you! I should really get to know that document better.

@mike.maxwell I've worked around this in my code for now by having the built-in parser parse this with the encoding changed to F2 and then unpacking the struct separately.

Here's a quick and dirty way of working around this:

In parse():

Map msgMap = null
if(description.indexOf('encoding: 4C') >= 0) {
  msgMap = unpackStructInMap(zigbee.parseDescriptionAsMap(description.replace('encoding: 4C', 'encoding: F2')))
} else {
  msgMap = zigbee.parseDescriptionAsMap(description)
} 

unpackStructInMap method:

Map unpackStructInMap(Map msgMap) {
    // This is a LIMITED implementation, it only does what is needed by any of my drivers so far
    // This is NOT optimized for speed, it is just a convenient way of doing things
    msgMap['encoding'] = '4C'
    List<String> values = msgMap['value'].split("(?<=\\G..)")
    Integer numElements = Integer.parseInt(values.take(2).reverse().join(), 16)
    values = values.drop(2)
    List r = []
    while(values != []) {
        Integer cType = Integer.parseInt(values.take(1)[0], 16)
        values = values.drop(1)
        switch(cType) {
            case 16:
                // BOOLEAN
                r += Integer.parseInt(values.take(1)[0], 16) != 0
                values = values.drop(1)
                break
            case 32:
                // UINT8
                r += Integer.parseInt(values.take(1)[0], 16)
                values = values.drop(1)
                break
            case 33:
                // UINT16
                r += Integer.parseInt(values.take(2).reverse().join(), 16)
                values = values.drop(2)
                break
            case 34:
                // UINT24
                r += Integer.parseInt(values.take(3).reverse().join(), 16)
                values = values.drop(3)
                break
            case 35:
                // UINT24
                r += Long.parseLong(values.take(4).reverse().join(), 16)
                values = values.drop(4)
                break
            case 36:
                // UINT40
                r += Long.parseLong(values.take(5).reverse().join(), 16)
                values = values.drop(5)
                break
            default:
                throw new Exception("The STRUCT used an unrecognized type: $cType (0x${Long.toHexString(cType)})")
        }
    }
    msgMap['value'] = r
    return msgMap
}
1 Like