Skip to content

Add LG CordZero A9 stick vacuum HWWA9K_F2 - #116

Open
tunarabuuu wants to merge 1 commit into
anszom:masterfrom
tunarabuuu:dev-vacuum
Open

Add LG CordZero A9 stick vacuum HWWA9K_F2#116
tunarabuuu wants to merge 1 commit into
anszom:masterfrom
tunarabuuu:dev-vacuum

Conversation

@tunarabuuu

Copy link
Copy Markdown
Contributor

Fourth of the per-device-class splits from #111.

HWWA9K_F2 — an AA..BB ThinQ2 stick vacuum (deviceType 504). The byte layout was recovered by injecting single-byte-changed state frames through the management API and reading the LG cloud's decode back, one offset per observation. The appliance sends the current record last (with the previous record ahead of it after a setting changes), so the trailing record is decoded.

Addressing your review notes from #111:

  • processAABB — reads go through the base class's processAABB now; the frame arrives with its leading AA/length and trailing CRC/BB stripped, so byte 0 is the class tag and the record offsets need no whole-frame adjustment. The 44-byte capability list the appliance also sends is still rejected, by its record-count guard.
  • logging utility — the console.warn calls now use log(...).
  • header comment — trimmed, and dropped a stray absolute path to a capture file on my machine.

All labels are English. npm test passes; tsc clean.

An AA..BB ThinQ2 stick vacuum (deviceType 504). The byte layout was recovered
by injecting single-byte-changed state frames through the management API and
reading LG cloud's decode back, one offset per observation. The appliance
sends the current record last, with the previous record ahead of it after a
setting changes, so the trailing record is decoded.

Reads go through the base class's processAABB — the frame arrives with its
leading AA/length and trailing CRC/BB stripped, so byte 0 is the class tag and
the record offsets need no whole-frame adjustment. The 44-byte capability list
the appliance also sends is rejected by its record-count guard. Warnings go
through the logging utility.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BluSyn pushed a commit to BluSyn/rethink-rs that referenced this pull request Aug 21, 2026
Port from upstream Node PR #116
(anszom/rethink#116). AABB class 0xD2 14-byte
records (trailing record is current); F024 writes for suction, mop,
melody, volume, and brightness. Fixtures are the 2026-07-28 capture
and LG-cloud oracle injections.

@anszom anszom left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add a note in the README.

allowExtendedType({
...HADevice.config(meta, { name: 'LG Stick Vacuum' }),
components: {
status: sensor('status', 'State', { icon: 'mdi:robot-vacuum' }),

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the enum sensors should use device_class: 'enum', options: ..., just like other devices do.

Comment on lines +181 to +182
const enumOf = (table: Record<number, string>, raw: number) =>
table[raw] ?? (raw === 0 || raw === 0xff ? 'Unknown' : `Code ${raw}`)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we switch to device_class: 'enum', then HA will reject non-matching enums, only the 'unknown' string will be an accepted fallback.

Comment on lines +309 to +312
// The appliance echoes a two-record state frame within a second, so the entity
// settles on what it actually took; this only keeps the UI from snapping back first.
this.publishProperty(prop, mqttValue)
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would setting optimistic: true be a suitable replacement for this logic?

* The base class strips the AA/length prefix before handing the body on, but every offset
* above is stated against the whole frame, the way the probes recorded them, so the frame is
* taken here unstripped.
*/

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outdated comment. Please scan the whole PR for outdated comments and remove them or bring them up-to-date. A misleading comment is worse than no comment at all :)

import HADevice from './base'
import AABBDevice from './aabb_device'

/*

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please shorten the header block. LLMs do have a tendency to leave a history trail of older attempts & revisions, this is not useful here.

/** processAABB receives the frame with its leading AA/length and trailing CRC/BB stripped,
* so byte 0 is the class tag and there are no leftover header/trailer bytes to skip. */
const HEADER_LEN = 2
const TRAILER_LEN = 0

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this serves no purpose now, remove the constant and the useless - TRAILER_LEN expressions

Comment on lines +178 to +190
test('every user-facing name and option is English', () => {
const { ha, thinq } = makeDevice()
thinq.emit('data', STATE)
const components = ha.devices[DEVICE_ID].config!.components as Record<string, { name?: string | null }>
for (const [key, comp] of Object.entries(components)) {
if (comp.name == null) continue
assert.match(comp.name, /[A-Za-z]/, `${key} name is English: ${comp.name}`)
}
const p = ha.devices[DEVICE_ID].properties
for (const key of ['status', 'clean_mode', 'suction_force', 'battery', 'nozzle', 'volume', 'brightness']) {
assert.match(String(p[key]), /[A-Za-z]/, `${key} value is English: ${p[key]}`)
}
})

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/[A-Za-z]/ will happily accept non-english words :) Just drop the test.

Comment thread cloud/ha_bridge.ts
['F3L2CYU__']: F3L2CYU__, // LG front-load washer
['RV13B6BSD_D_US_WIFI']: RV13B6BSD_D_US_WIFI, // LG electric dryer
WTL_FXU_BDV_NA_01, // LG WashTower
HWWA9K_F2, // LG CordZero A9 stick vacuum (deviceType 504)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the deviceType 504 to the list in panel.js too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants