Skip to content

Add LG RH90V9_WW heat pump dryer device class#55

Open
alexw23 wants to merge 10 commits into
anszom:masterfrom
alexw23:add-rh90v9-ww
Open

Add LG RH90V9_WW heat pump dryer device class#55
alexw23 wants to merge 10 commits into
anszom:masterfrom
alexw23:add-rh90v9-ww

Conversation

@alexw23

@alexw23 alexw23 commented May 24, 2026

Copy link
Copy Markdown

Add LG RH90V9_WW heat pump dryer device class

Adds a full device class for the LG RH90V9_WW heat pump dryer, reverse engineered from wire captures against the AABB packet protocol.

Entities

Controls

  • Power switch (OFF always works; ON gated behind Safety Lock)
  • 🔒 Safety Lock select — gates Power On and Remote Start per session
  • Cycle select — all 15 base courses from modelJson
  • Dry level select — dynamic options per cycle schema
  • Eco hybrid select — dynamic options per cycle schema
  • Anti-crease switch
  • Delayed end select (Off, 3h–19h)
  • Start button
  • Pause button

Sensors

  • Run state, Process state
  • Remaining time, Initial time
  • Remote start armed (binary sensor)
  • Run completed (binary sensor)
  • Error state (binary sensor) + Error message
  • Downloaded cycle ID (diagnostic)

Protocol

Full field map confirmed from wire captures and official modelJson:

Field Description
Bd[0] Run state
Bd[1-2] Remaining hours/minutes
Bd[3-4] Initial hours/minutes
Bd[5] Cycle ID
Bd[6] Error code
Bd[7] Dry level
Bd[8] Eco hybrid
Bd[9] Process state
Bd[10] Reservation hours (delayed end)
Bd[14] Flags: 0x02=anti-crease
Bd[15] Flags: 0x01=remote start armed
Bd[20] Active downloaded/SmartCourse ID

Start command inner layout (F0 26 opcode, 16 bytes):

Byte Description
[2] Cycle ID
[3] Dry level
[4] Eco hybrid
[8] Reservation hours (0=off, 3-19=delayed end)
[11] Anti-crease (0x00=off, 0x02=on)
[12] Operation (0x03=start, 0x01=resume/update)
[14] SmartCourse ID (0x00 for base courses)

Features

Per-cycle schema — dry level and eco hybrid options filter dynamically when cycle changes, with defaults applied from modelJson. Covers all 15 base courses.

Stale value correction — packet values validated against schema on every state update. Invalid or stale values (e.g. dry level carrying over from a previous cycle) are corrected to schema defaults rather than published to HA.

Downloaded cyclesDOWNLOADED_CYCLES table maps SmartCourse wire IDs to friendly names and schema defaults. Two IDs confirmed from captures (GYMCLOTHES=0x66, DEODORIZATION=0x6b), fourteen others commented out awaiting capture. A downloaded_cycle_id diagnostic sensor shows the raw hex ID to help users identify new cycles.

Selector debounce — 10s lock prevents incoming state packets from overwriting in-progress HA selector edits.

Safety Lock — gates Power On (wire-level confirmed working via F02A0100, rejected by LG cloud) and Remote Start per session. Resets to locked on every restart.

Confirmed captures

All field mappings verified against real packet captures from an RH90V9_WW unit running firmware 2.10.123.

@anszom

anszom commented May 25, 2026

Copy link
Copy Markdown
Owner

Thank you for the PR. It seems quite complex, so I'll ask you for a clarification on a few points before doing a proper review.

  1. What is the purpose of the safety lock? I'm familiar with a similar feature on various device - the primary principle being that the safety lock can only be toggled on the device UI. There is a mention of "see docs for details", but there are no docs :(
  2. What is "selector lock" ?

It's quite obvious that the PR message was written by a LLM. I have nothing against that, but I'd like the responses to be authored by an actual person who understands the code :)

@alexw23

alexw23 commented May 25, 2026

Copy link
Copy Markdown
Author

It's more comprehensive than complex. I gained access to the schema from the LG API so I was able to build out the full set of features. I will document this process shortly so others may be able to add in other types of LG appliances.

And yes LLM is great for documenting and summarising the whole build but can miss the human aspect at times.

As for your questions, LG prevent "power on" in their apps for all their dryers. So in the app you can turn it off but you can't turn it back on. However the machine does respond to this command regardless. So I added the ability to turn the machine on, but it's hidden behind the "safety lock".

There's a situation where someone might turn on the dryer and not be aware, say if they had the button on a dashboard etc. This could be a fire risk etc.

So just being overly cautious here. To turn it on you'd need to first turn off the safety lock, and it resets each power cycle, so it's always considered safe.

Re selector lock / this is purely a UX debounce, nothing safety related. The dryer sends state packets every few seconds. If a user changes a selector in HA (e.g. switches cycle from Cotton to Mixed Fabric), the next incoming state packet would immediately overwrite their selection before they hit Start.

The selector lock suppresses state packet overwrites for 10 seconds after a user edit, giving them time to configure and start. It’s internal to the device class, no HA entity, not user-facing at all.

I’ll write some documentation shortly on this module and pop it in the PR. Including the statement around safety.

I think the long term question is - how much of this can be reused in a base dryer class. Right now it’s hard to answer, without some packets from other machines. If we spot similar packets on other machines than I’ll look to move some of this logic into a base class.

Any other questions let me know. And must say a huge thanks for getting this repo up and running in the first place.

@anszom

anszom commented May 25, 2026

Copy link
Copy Markdown
Owner

I gained access to the schema from the LG API so I was able to build out the full set of features. I will document this process shortly so others may be able to add in other types of LG appliances.

Great! This could be really useful.

@anszom

anszom commented May 25, 2026

Copy link
Copy Markdown
Owner

As for your questions, LG prevent "power on" in their apps for all their dryers. So in the app you can turn it off but you can't turn it back on.

You mean, you can't turn the device on via the app, ever? Or is there a time window during which it is allowed?

The selector lock suppresses state packet overwrites for 10 seconds after a user edit, giving them time to configure and start. It’s internal to the device class, no HA entity, not user-facing at all.

Hmm, tell me if I understand this correctly.

The device sends its state periodically.
The app can also set new parameters.
a) If a new app command ("set dry level to 2") arrives at just the wrong moment, the device's old state will be applied on top of this "dry level = 1". In this case, a 10 second debounce timer seems quite long...
b) The app-provided state is never reflected in the device-initiated packets, and the app command "set dry level to 2" will eventually be overwritten by the device, no matter what. In this case, a 10 second timer seems quite short...

Is it (a), (b), or something else?

@Drafteed

Drafteed commented May 25, 2026

Copy link
Copy Markdown
Collaborator

Hi!

  1. Regarding the safety lock - this may sound a bit weird, but I consider it an unnecessary complication and a very non-standard practice. At least, I've never seen anything like this in any Home Assistant integration. I think it would be better to handle accidental actions in the HA UI itself by restricting access via permissions, using conditions in cards to hide controls, etc.

  2. The idea of combining sensors (those showing the current course parameters) with control entities, and doing it in a way where the user only has 10 seconds, feels a bit unusual to me as well and also generally a non-standard practice. Usually, this is done via custom service / MQTT call where all course parameters are sent at once.

…N payload

- Remove Safety Lock entity, selector lock machinery, and all selectedXxx state
- cycle/dry_level/eco_hybrid/reservation become read-only sensors; anti_crease
  keeps its switch (state reflects Bd, toggle updates start fallback)
- processAABB publishes Bd values directly without schema correction or lock checks
- start button accepts optional JSON payload with cycle/dry_level/eco_hybrid/
  reservation/anti_crease; missing fields fall back to last known Bd values
- buildStartCommand still validates against CYCLE_SCHEMA and silently corrects
  invalid combos with a warning
- Power ON no longer requires Safety Lock; wake sequence (F0 25 + F0 2A) unchanged
- Update tests to match: remove selector/lock tests, add JSON payload tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Rexz34

Rexz34 commented May 28, 2026

Copy link
Copy Markdown

You mean, you can't turn the device on via the app, ever? Or is there a time window during which it is allowed?

Just to add. I'm not sure about bypassing the 'safety lock' as @alexw23 stated but the whole idea is, on the front of the dryer you can enable "remote start", which then gives you access to all the remote functions of starting the dryer or pausing the dryer to change a mode or setting. Once the dryer is paused, you have a limited time to start the dryer again from the app before it auto turns off the dryer to power save. Once this happens the remote start feature is disabled again and you have to be physically in front of the dryer to enable it again.

As far as I'm aware, with the remote start feature enabled, the dryer will stay on until you start a program but once paused will auto timeout and turn off.
I did notice the dryer still remains connected via wifi even when the machine is off (ip address was pingable).

Another thing to add is, my dryer is the slightly newer version of this with touch buttons instead of push buttons for the power and start/pause. All of the options @alexw23 has found seem to be the same for the updated version too. So rather than maybe having duplicate code for all the different models of dryer (I think LG has a lot based on country and slight variations in colours), maybe multiple models could be grouped together?

@Rexz34

Rexz34 commented May 28, 2026

Copy link
Copy Markdown

Regarding the safety lock - this may sound a bit weird, but I consider it an unnecessary complication and a very non-standard practice.....

I think this is more built into LG dryers than a a feature @alexw23 is coding. Once remote start, is enabled on these machines, the door locks. The idea is that if you were to start the machine remotely, then the door has been locked since you enabled it whilst at the machine, so nothing could have opened or climbed inside in the mean time (I used to have a dog that liked opening our old dryer door). Obviously turning the dryer on remotely, you would have no idea what had happened.

@anszom

anszom commented May 28, 2026

Copy link
Copy Markdown
Owner

I'm not sure what @Drafteed had meant, but in my opinion, we should neither fight against the lockout built into the device, nor implement our own on top of that.

@Drafteed

Copy link
Copy Markdown
Collaborator

nor implement our own on top of that.

That’s exactly what I meant :)

@anszom

anszom commented May 28, 2026

Copy link
Copy Markdown
Owner

Another thing to add is, my dryer is the slightly newer version of this with touch buttons instead of push buttons for the power and start/pause. All of the options @alexw23 has found seem to be the same for the updated version too. So rather than maybe having duplicate code for all the different models of dryer (I think LG has a lot based on country and slight variations in colours), maybe multiple models could be grouped together?

What is the Thinq model name of your dryer? If it's RH90V9_WW, then we will assume that the protocol is bit-identical (until proven otherwise). If it's different, then .. we can still get lucky and reuse the same implementation if the protocol matches. Otherwise we can reuse some parts (see fridge_common for an example)

@Rexz34

Rexz34 commented May 28, 2026

Copy link
Copy Markdown

What is the Thinq model name of your dryer?

So my model is FDV1110B. I've no idea how LG model numbers work but they seem to change based on country and then slight appearance changes/updates.

I've yet to test any of this code, but was brought here after the latest LG debacle with account banning. I'm a great believe in having more control over IOT and stopping things 'calling home'

@anszom

anszom commented May 28, 2026

Copy link
Copy Markdown
Owner

So my model is FDV1110B

This is the customer-visible model. The "techhnical" model name is visible in rethink logs, both during provisioning & normal operation. I guess it's also available through the official API but I haven't checked.

@Drafteed

Copy link
Copy Markdown
Collaborator

So rather than maybe having duplicate code for all the different models of dryer (I think LG has a lot based on country and slight variations in colours), maybe multiple models could be grouped together?

I think we could accumulate a certain number of devices first, and then consolidate them into a common builder/base class where model-specific features/capabilities are selected for each device.

@Rexz34

Rexz34 commented May 28, 2026

Copy link
Copy Markdown

So my model is FDV1110B

This is the customer-visible model. The "techhnical" model name is visible in rethink logs, both during provisioning & normal operation. I guess it's also available through the official API but I haven't checked.

Ah in that case I'll have to do a little more digging. The official app doesn't give any other information that the visible model number and some software version.

@Rexz34

Rexz34 commented May 28, 2026

Copy link
Copy Markdown

Ok so I checked the home assistant SmartThinQ LGE Sensors integration and it is indeed the same model number: RH90V9_WW-Dryer (DRYER) by LG

Maybe we do have the same dryer? Or maybe they do use the same technical model across different dryers. Maybe @alexw23 can enlighten us with what his customer model of dryer he has.

@anszom

anszom commented Jun 2, 2026

Copy link
Copy Markdown
Owner

@alexw23 I see that you've pushed some changes. It would be helpful if you answered my previous questions and what's the rationale behind these changes. That is - if you're still interested in merging this.

@OiledAmoeba

Copy link
Copy Markdown

I own the same type of tumble dryer. So I tried Alex' code as a simple new file to cloud/devices and referred the new file in cloud/ha_bridge.ts. (Short npm run build is required, because of the design by the precompiled project)
For me, the dryer works perfectly well in HomeAssistant (and since it'uses MQTT, also in FHEM).
For example, my last used "Downloaded Program" was "Rainy Day". Alex' code showed exactly that program over MQTT.
From my perspective, the PR should be accepted/approved, or at least Alex' .ts for the device class to use it for the build process.
The use of Alex' standalone device class file within your flattened new folder structure causes no problems in my installation. By accepting only the device class file, your project would be able to support this type of dryer. And later you can discuss Alex' extra edits ;-)

@alexw23

alexw23 commented Jun 13, 2026 via email

Copy link
Copy Markdown
Author

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.

5 participants