Add support for the F3L7CYK5W_US_WIFI front-load washer - #135
Open
Danimal4326 wants to merge 1 commit into
Open
Conversation
Shares the AABB record layout of F3L2CYU__ (25-byte record led by a 0x18
marker) but is deliberately a separate handler rather than an alias, for four
reasons:
* The course table is different -- not a superset or a renaming, a different
assignment. It disagrees at nine of twelve dial positions; 0x06 is NORMAL
here, where the F3L2CYU__ table says Heavy Duty. This was the trap: a first
pass at this driver inherited the sibling's table on the assumption that a
shared record layout implies a shared course enum, and mislabelled almost
every position. There is a regression test pinning it.
* rec[11] packs two fields: the low nibble is a live rinse count (it
decrements as rinses complete) and the high nibble is the extra-rinse
setting. F3L2CYU__ reads only the high nibble and documents the low nibble
as a constant, which is not true of this model.
* rec[15] carries two option bits F3L2CYU__ does not have: child lock (0x01)
and Rinse+Spin (0x20). Both F3L2CYU__ and the RV13B6BSD dryer document
child lock as unfindable in device frames, "appears cloud-side only" -- it
is simply at a different bit here.
* rec[4:6] is a persistent initial-cycle-time estimate that pins when a cycle
starts while rec[2:4] counts down. F3L2CYU__ states that model has no such
field and reuses the countdown bytes for both purposes.
Also decoded: the delay reserve clock, TCLCount (rec[22]), loadLevel (rec[24]),
and phase 0x15 = ADD_DRAIN, the door-unlocked state behind the Add Garments
button. Exercising Add Garments also confirmed phase 0x06 = PAUSE, which
F3L2CYU__ lists as unverified.
0xE2 frames are deliberately left undecoded, and there is a test asserting so.
They are 28 bytes with a valid 0x18 record at offset 3, structurally identical
to 0xEB, but they are emitted in a burst AFTER a cycle finishes and replay a
stale snapshot of that cycle's start -- decoding them knocks the machine back
from Complete to Sensing every time a wash ends.
Every offset was confirmed by capturing live traffic with
tools/rethink-capture.ts --cloud while driving the physical control panel, and
matching each byte change against the LG cloud field that moved with it. One
note for anyone repeating this: filter cloud notifications on the capture tool's
matchesDevice flag. A dryer on the same account emits washerDryer updates whose
keys collide with the washer's (state, preState, temp), and merging those in
silently corrupts the mapping.
Omitted rather than guessed: remote start (the cloud reports it, but it moved in
lockstep with the rec[16] door-lock bit so no independent bit could be
isolated), TurboWash (this model has no such button, so the sibling's rec[15]
bit 0x80 is left unmapped), the Signal setting (this model transmits no frame at
all when it changes, verified both mid-cycle and idle), and error codes.
All test fixtures are real captured frames, with the confirming cloud field
named in a comment above each one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a driver for the LG F3L7CYK5W_US_WIFI front-load washer (deviceType 201, thinq2).
It shares the AABB record layout of
F3L2CYU__— 25-byte record led by a0x18marker,0xECtwo-record status frame with record B at offset 29,0xEBsingle record at offset 3 — but is deliberately a separate handler.Why it isn't an alias
The course table is different. Not a superset, not a renaming — a different assignment, disagreeing at nine of twelve dial positions.
0x06isNORMALhere, where theF3L2CYU__table says Heavy Duty.This was the trap worth flagging for anyone adding a sibling model: a first pass at this driver inherited the sibling's table on the assumption that a shared record layout implies a shared course enum. It doesn't. Every position is now confirmed against the cloud's
apCourseFLUpper25inchBaseUSand cross-checked against the printed control panel, with a regression test pinning it.Three more divergences:
rec[11]packs two fields — low nibble is a live rinse count (it decrements as rinses complete), high nibble is the extra-rinse setting.F3L2CYU__reads only the high nibble and documents the low nibble as a constant, which isn't true here.rec[15]has two option bits the sibling lacks: child lock (0x01) and Rinse+Spin (0x20). BothF3L2CYU__and theRV13B6BSDdryer document child lock as unfindable in device frames — it's simply at a different bit on this model.rec[4:6]is a real initial-cycle-time estimate that pins when a cycle starts whilerec[2:4]counts down.F3L2CYU__states that model has none and reuses the countdown bytes for both purposes.Also decoded: the delay reserve clock,
TCLCount(rec[22]),loadLevel(rec[24]), and phase0x15=ADD_DRAIN, the door-unlocked state behind the Add Garments button. Exercising Add Garments also confirmed phase0x06=PAUSE, whichF3L2CYU__currently lists as unverified.0xE2 frames are intentionally not decoded
Worth calling out because it looks like an oversight.
0xE2is 28 bytes with a valid0x18record at offset 3 — structurally identical to0xEB— but it's emitted in a burst after a cycle finishes and replays a stale snapshot of that cycle's start (phase Sensing, the pre-run estimate, the pre-incrementTCLCount). Decoding it knocks the machine back from Complete to Sensing every time a wash ends. There's a test asserting it stays ignored.How it was confirmed
Live capture with
tools/rethink-capture.ts --cloudwhile driving the physical panel: full dial sweep, every option toggled one at a time, a delay-wash set and cancelled, and complete cycles including Add Garments mid-run.One note for anyone repeating this: filter cloud notifications on the capture tool's
matchesDeviceflag. A dryer on the same account emitswasherDryerupdates whose keys collide with the washer's (state,preState,temp), and merging those in silently corrupts the mapping.Omitted rather than guessed:
rec[16]door-lock bit throughout, so no independent bit could be isolatedrec[15]bit0x80is left unmapped rather than inheritedTesting
All fixtures are real captured frames, each with the confirming cloud field named in a comment above it. Full suite passes (360 tests). The driver has been running against the physical appliance via MQTT discovery in Home Assistant, reporting 22 entities that track the panel through complete cycles.
🤖 Generated with Claude Code
https://claude.ai/code/session_01EdP8L3BhHJFWX1ZKfQLCcM