v0.7.4 — the 0x52 command frame, corrected
A fix for hubless WiFi-native locks, from a source-level diagnosis rather than a capture.
Confirmed working. Verified on two
PGK728WRHK(Lockly Visage) on firmware 1.14.31 and 3.00.24 — lock and unlock both succeed over the broker, with the state change coming back on the client topic. Reported on issue #3 within hours of release. This release shipped as untested; it is not any more.Take v0.7.5 instead: same frame, without the log messages that still called these locks unsupported.
What was wrong
Locks with no hub reach the lock over the MQTT broker and are then refused by the lock itself with 0xFF, "wrong password". That looked like a credential problem. It was not: the command frame was malformed, and the lock was reading the credential fields at the wrong offsets.
NewUnlockCmd's isSupport82Cmd branch — the 0x52 command every WiFi-native model uses — differs from the ordinary 0x22 layout in three places, and this integration built none of them:
- The field after the password is two bytes, not one.
getUserId()runs both of its branches throughgetCmdLenString, which is little-endian 16-bit. For the lock's owner the value is0:isOwner()is defined asuserId == 0 && adminId == 0, and nothing in the app gives a host a non-zero user ID — the cloud restore path copies sixty-odd fields out of the device record and never that one. A byte short here left the action, hub flag and trailing field each read a byte early, which is enough on its own to fail a credential check that would otherwise pass. - The trailing field is the phone's clock, not the lock's nonce.
isSupportTimestamp()is true for all of these models, so the app sendsDataUtils.p(System.currentTimeMillis())— eight bytes little-endian — and never replays the stored value. - The frame's encryption type is
0xB, not5. The0x52branch overridesgetEncryptType()withisHost() ? 11 : isLongTerm() ? 13 : 12.
All three ship together. Two of them change the frame's length, so correcting one without the others only moves the corruption.
Credit
Diagnosed by @Dei381rcr against Lockly Android 3.3.4, including the getCmdLenString and encryptType findings and the observation that 0xFF was not proof the credential was wrong. Confirmed here against app 3.2.9 and Lockly Home 1.4.8 before shipping, along with the missing piece — where BluetoothBean.userId comes from for a host.
How to help
If you have a PGK728WRHK, PGD728FG25 or another hubless Lockly, update and try lock and unlock once each with debug logging on, then paste the log on #2 or #3:
logger:
default: warning
logs:
custom_components.lockly: debugEither it works, or the error byte tells us which field is still wrong. Both outcomes are useful.
Unchanged
Hub-attached locks are untouched: the 0x22 frame is byte-for-byte identical to 0.7.3, and the status query and QueryPwd147 keep encryption type 5 — QueryPwd147Cmd asks getTenantAccessEncryptType(), which falls through to getEncryptType() for a host, so the 0xFA those locks return to 0x93 is still unexplained.
Vision locks now send encryption type 0xB on the 0x22 path as well, which is what (isVision() && isHost()) does in the app. No Vision hardware has been tested against this integration, so if a Vision lock stopped working with this release, please say so on an issue.