Skip to content

Complete RTL8720cm/BK7234 provisioning: local handshake, generic fallback, UI improvements - #92

Open
XanderLuciano wants to merge 5 commits into
anszom:masterfrom
XanderLuciano:microwave-provisioning
Open

Complete RTL8720cm/BK7234 provisioning: local handshake, generic fallback, UI improvements#92
XanderLuciano wants to merge 5 commits into
anszom:masterfrom
XanderLuciano:microwave-provisioning

Conversation

@XanderLuciano

Copy link
Copy Markdown
Contributor

Summary

This PR builds on #81 (by @aaronsb — publicKey whitespace fix + generic device fallback) and extends it with fixes and features discovered while provisioning 4 new LG appliances (microwave, gas range, fridge, dishwasher) fully locally.

All credit to @aaronsb for identifying the root causes and initial implementations. We've reimplemented their key fixes and added significant improvements on top.


What's Included

1. PublicKey whitespace fix (reimplemented from #81)

The hardcoded setup publicKey has tab-indented base64 lines. RTL8720cm "CLIP" modules use a strict PEM parser — with tabs, getDeviceInfo's RSA encrypt step fails silently (encryptRes:ffff) and the device loops on /route indefinitely. Stripping the tabs fixes it. Confirmed on WMVEL2137 (microwave), WLDGL6924 (gas range), 2REF11EBIW__4 (fridge), and N15 (dishwasher — Beken BK7234, which also needs clean PEM).

2. Generic device fallback (reimplemented from #81)

Unknown modelIds were previously dropped entirely — no enrollment, no HA mapping. A new GenericDevice class now catches any unknown ThinQ2 device, enrolls it, and publishes raw packet hex to a diagnostic HA sensor. This turns unsupported models from silent dead ends into observable data sources, and keeps the device online.

3. Local-only provisioning fix (novel)

After analyzing the bridge mode handshake between the device and LG's real cloud, we discovered the device needs provisioningType: "deploy" (not "preDeploy") in the completeProvisioning response, plus a resp_timesync before completeProvisioning_ack — otherwise the chipset stays in setup mode and never exits to normal operation.

Three changes in cloud/thinq2/provisioning.ts and device.ts:

  • generateDeployResponse always responds with provisioningType: "deploy"
  • req_timesync handler now checks client.deployMsg (exists during provisioning) instead of client.deviceObj (only exists AFTER ack)
  • timeSyncRequest publishes directly via the broker instead of via deviceObj.send() (deviceObj doesn't exist yet)

Result: factory reset → provision → device completes full handshake locally → chimes → exits setup. No bridge mode needed.

4. Auto-detect DeviceType for bridge mode

DeviceType (101, 204, 301, 302, etc.) is in the preDeploy payload but wasn't being stored in Metadata. Now populated during completeProvisioning, so the management UI auto-fills it when enabling bridge mode — no manual entry needed for any device.

5. UI improvements

  • Friendly device names: Added a mapping from model IDs to human-readable labels (e.g. WMVEL2137 → "Microwave / Hood", N15 → "Dishwasher"). Unknown models fall back to raw ID.
  • Bridge button state fix: Optimistic state update after bridge toggle success. Previously, the WebSocket update sometimes arrived after refreshUI(), causing the checkbox to revert to the wrong state — especially visible when adding a new device.
  • Added device types 204, 301, 302 to the autocomplete picker.

6. Raw HTTP logging

Safe try/catch-wrapped request logging middleware for both ThinQ1 and ThinQ2 HTTPS servers. Logs headers, method, URL, and parsed body — never throws.


Testing

Tested against 4 previously-unsupported LG appliances, all now fully provisioned and controllable locally:

Appliance Model ID Type Chipset FW
Microwave / Hood (MVEL2033F) WMVEL2137 302 RTL8720cm CLIP v1.9.210
Gas Range (LDGL6924S) WLDGL6924 301 RTL8720cm CLIP v1.9.223
Fridge (LRFLS3206S) 2REF11EBIW__4 101 RTL8720cm CLIP v1.9.196
Dishwasher (LDNPM545S) N15 204 Beken BK7234 CLIP v1.9.226

Local provisioning: All 4 devices complete the full handshake locally (no cloud) — deploy → timesync → ack → normal mode.

Bridge mode: All 4 register with LG cloud successfully. DNS forwarder + /etc/hosts trick allows simultaneous local-only + bridge operation.

Local control (fan/light): Fully reverse-engineered and working via direct MQTT publish — no app, no cloud.

Raw packet capture: All 4 streaming TLV hex to HA diagnostic sensors via the generic fallback.


Additional Discoveries

Bridge mode + DNS coexistence: When using a DNS forwarder to redirect common.lgthinq.com → local server, bridge mode can't reach LG's real cloud. Fix: add the real CloudFront IP for common.lgthinq.com to /etc/hosts on the rethink host. The NUC resolves to the real LG cloud (for bridge mode API calls) while appliances still resolve to rethink (for local-only mode).

LG registration conflict (error 9006): When a device was previously registered to the LG account, the addDevice call returns resultCode: 9006 ("already registered"). Retrying after the removeDevice step succeeds resolves it.

Beken BK7234 dishwasher: Uses the same CLIP provisioning flow as RTL8720cm. Provisioned without changes. Uses JITP (PERF_jitp step) and a different package structure — bridge mode needs the LG app to finish/time out its registration attempt before taking over.

Per anszom#81: tab-indented base64 in setup publicKey
breaks RSA encrypt on RTL8720cm/CLIP modules. Device silently
fails getDeviceInfo, can't build cert request, loops on /route.

Also: add raw-logger.ts, raw HTTP logging in rethink-cloud.ts
Per anszom#81: when a ThinQ2 device with no specific class
connects, enroll it via GenericDevice instead of dropping it.
GenericDevice captures raw packet hex to HA diagnostic sensors,
enabling protocol reverse-engineering from live data.

Changes:
- cloud/devices/generic.ts: loose-capture class, never throws
- cloud/ha_bridge.ts: fallback to GenericDevice for unknown modelId
…oning_ack

The RTL8720cm CLIP firmware needs provisioningType: 'deploy'
(not 'preDeploy') and resp_timesync BEFORE sending
completeProvisioning_ack to exit setup mode cleanly.

Changes:
- provisioning.ts: always respond with provisioningType 'deploy'
- device.ts: req_timesync handler checks deployMsg (not deviceObj)
  since it fires before completeProvisioning_ack
- device.ts: timeSyncRequest publishes directly via broker
  instead of via deviceObj.send() (deviceObj doesn't exist yet)
- device.ts: device_packet handler uses deployMsg?.did for early packets

With these, devices complete provisioning fully locally — no bridge
mode needed. Bridge mode remains available for optional LG cloud
registration.
DeviceType (301, 302, etc.) is in the preDeploy payload but
wasn't being stored in Metadata. Now populated during
completeProvisioning, so the management UI auto-fills it
when enabling bridge mode — no manual entry needed.
- Add FRIENDLY_NAMES mapping (modelId -> human-readable label)
- Show friendly label in device table with model fallback
- Add dishwasher and microwave to autocomplete type picker
- Fix bridge button state: optimistic update after success
  (prevents button showing wrong state when WebSocket
   update arrives after async bridge call completes)
@maciejsszmigiero

Copy link
Copy Markdown
Collaborator

Please read this comment on the #81 - it looks like it wasn't addressed here.

Comment thread cloud/thinq2/device.ts
modelId: client.deployMsg.kind,
modelName: client.deployMsg.data?.appInfo?.modelName,
swVersion: client.deployMsg.data?.appInfo?.softVer,
deviceType: client.deployMsg.data?.appInfo?.DeviceType,

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.

I would have used this already, but my AC sends: "DeviceType":"". But it's good to have a shorter path for better-behaved devices.

@anszom

anszom commented Jul 21, 2026

Copy link
Copy Markdown
Owner

This PR spreads a variety of topics, so I'm tackling it in small steps:

  1. The publicKey fix was already merged before
  2. I'm leaving out the "generic device fallback", as I'm still not convinced how it would be useful.
  3. I wasn't able to test the completeProvisioning steps - I will get back to it later.
  4. Auto-detect DeviceType - cherry-picked.
  5. UI improvements, except FRIENDLY_NAMES - cherry-picked
  6. FRIENDLY_NAMES - In the current form it only contains four model names, so not very useful in the general case. A better approach would be to add a friendly model name to each device definition and then provide the frontend with a more complete map.

Local control (fan/light): Fully reverse-engineered and working via direct MQTT publish — no app, no cloud.

I think this wasn't included in this PR?

@anszom

anszom commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Update: i've also merged the timesync changes (see c7a9a00). I've deliberately excluded the change from preDeploy to deploy, because this doesn't match the responses sent by the official backend.

Can you share the captures which indicated that this change is needed for your device?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants