Add LG Styler ST_B_E4H01Y_APL - #115
Conversation
95b0228 to
250d3a1
Compare
Port from upstream Node PR #115 (anszom/rethink#115). AABB class 0x31 with 40-byte records (last record is current). Field map and F024/F026 command frames from 2026-07-28 LG-cloud oracle and convert/control captures, including Standard/Quick start blocks.
| 17: '8000000000000000000000000000001e780000000000000000000000000000000000000000', // Timed Dry 30 | ||
| 18: '8000000000000000000000000000003c780000000000000000000000000000000000000000', // Timed Dry 60 | ||
| 19: '8000000000000000000000000000005a780000000000000000000000000000000000000000', // Timed Dry 90 | ||
| 20: '8000000000000000000000000000003c780000000000000000000000000000000000000000', // Timed Dry 120 |
There was a problem hiding this comment.
This is identical to Timed Dry 60. Is this correct?
| // Replaced by the two settable halves above. Removing a component from the | ||
| // config is not enough to retire the entity Home Assistant already made — | ||
| // a config carrying nothing but its platform is what says "this is gone", | ||
| // and it has to carry that, or HA rejects the whole device payload. | ||
| night_care_window: { platform: 'sensor' } as ComponentInfo, |
There was a problem hiding this comment.
That entity was never exposed by a released version of rethink, so we don't need to include code to withdraw it.
| remaining: minutes('remaining', 'Time remaining', { icon: 'mdi:timer-sand' }), | ||
| total: minutes('total', 'Total time', { icon: 'mdi:timer-outline' }), | ||
| reserved_at: sensor('reserved_at', 'Reserved time', { icon: 'mdi:calendar-clock' }), |
There was a problem hiding this comment.
Please follow the naming conventions of other devices (remaining_time, reserve_time)
| ...HADevice.config(meta, { name: 'LG Styler' }), | ||
| components: { | ||
| power: toggle('power', 'Power', { icon: 'mdi:power' }), | ||
| status: sensor('status', 'State', { icon: 'mdi:hanger' }), |
There was a problem hiding this comment.
Other devices use the 'Status' label
| import HADevice from './base' | ||
| import AABBDevice from './aabb_device' | ||
|
|
||
| /* |
There was a problem hiding this comment.
This header is overly verbose. Keep only the most important protocol details, definitely drop mentions of older revisions that we have never seen.
| describe(MODEL_ID, () => { | ||
| /* | ||
| * Writable exactly where a command frame was captured off LG's own capability API and seen to | ||
| * take (CL-0000). Deliberately absent: `remoteControlStatus setRemoteControlType`, which |
There was a problem hiding this comment.
The fields claimed to be absent do appear to be present...
| const { ha } = makeDevice() | ||
| const components = ha.devices[DEVICE_ID].config!.components as Record<string, Record<string, unknown>> | ||
| for (const [name, comp] of Object.entries(components)) { | ||
| if (comp.unique_id === undefined) continue // a removal, not an entity |
There was a problem hiding this comment.
This line should not be needed if we remove the night_care_window reference entirely.
| test('the retired night-care summary is published as a removal', () => { | ||
| const { ha } = makeDevice() | ||
| const components = ha.devices[DEVICE_ID].config!.components as Record<string, Record<string, unknown>> | ||
| // Its two settable halves replace it, but dropping it from the config would leave the old | ||
| // entity behind, frozen at whatever it last read. | ||
| assert.deepEqual(components.night_care_window, { platform: 'sensor' }) | ||
| }) |
There was a problem hiding this comment.
This should not be needed if we remove the night_care_window reference entirely.
| energy: sensor('energy', 'Energy monitoring', { | ||
| icon: 'mdi:flash', | ||
| entity_category: 'diagnostic', | ||
| }), |
There was a problem hiding this comment.
Is this in a useful real-world unit?
| internal_light: sensor('internal_light', 'Interior light setting', { | ||
| icon: 'mdi:lightbulb-outline', | ||
| entity_category: 'diagnostic', | ||
| // LG's snapshot exposes styler.internalLightingTime but this model's | ||
| // modelJSON does not declare its value list, so the raw code is published | ||
| // rather than an invented label. 0 read back NO_SETTING, 1 read LIGHTING_TIME_0. | ||
| }), |
There was a problem hiding this comment.
What is the meaning of this entity? If it has only two values, would a binary_sensor be a better choice?
|
Also please add a README entry just as for the other devices. |
An AA..BB ThinQ2 garment-care unit (deviceType 203). 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. Reads go through the base class's processAABB — the frame arrives with its leading AA/FF and trailing CRC/BB already stripped, so the record offsets are two less than the whole-frame positions the probes recorded. Stray warnings go through the logging utility rather than console. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
All addressed, one commit, rebased on master.
442 tests passing (409 on master + 33 here), |
Third of the per-device-class splits from #111.
ST_B_E4H01Y_APL — an AA..BB ThinQ2 Styler / garment-care unit (deviceType 203). The byte layout was recovered by injecting single-byte-changed state frames through the management API and reading the LG cloud's own decode back, one offset per observation (three of the bytes are bitfields, split bit by bit).
Addressing your review notes from #111:
processAABBinstead of a customprocessData; the frame arrives with its leading AA/FF and trailing CRC/BB stripped, so the record offsets are simply two less than the whole-frame positions.console.warncalls now uselog(...).All labels are English.
npm testpasses;tscclean.