diff --git a/CHANGELOG.md b/CHANGELOG.md index 87d650cd..9a11a637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Fixes +- Midea: MSmartHome-Cloud-Login — Anfrage-Felder `format`/`clientType`/`platform` jetzt als Zahlen (statt Strings) gemäß Referenz; behebt die Cloud-Ablehnung „value is illegal". Fehlermeldungen führen nun den Midea-Fehlercode mit. + +--- + ## [1.103.0] — 2026-06-26 ### Features diff --git a/src/services/midea/mideaCloud.js b/src/services/midea/mideaCloud.js index f1f42cde..9a68cc0f 100644 --- a/src/services/midea/mideaCloud.js +++ b/src/services/midea/mideaCloud.js @@ -119,8 +119,11 @@ class MideaCloud { const body = opts.raw ? data : { appId: c.appId, src: c.appId, - format: '2', - clientType: '1', + // NUMERIC types per cloud.py BaseCloud.FORMAT=2 / CLIENT_TYPE=1 (NOT strings): + // the Midea cloud validates these strictly and answers "value is illegal" + // when they arrive JSON-encoded as "2"/"1". + format: 2, + clientType: 1, language: 'en_US', deviceId: this.deviceId, stamp: timestamp(), @@ -152,7 +155,9 @@ class MideaCloud { if (m.includes('2fa') || m.includes('verification') || m.includes('captcha')) { return new MideaCloudError(msg || '2FA required', 'MIDEA_CLOUD_2FA_REQUIRED'); } - return new MideaCloudError(msg || `cloud error ${code}`, 'MIDEA_CLOUD_ERROR'); + // Surface the Midea error code so an unverified cloud schema can be diagnosed + // from the user-visible message (e.g. "value is illegal (Midea-Code 1010)"). + return new MideaCloudError(`${msg || 'cloud error'} (Midea-Code ${code})`, 'MIDEA_CLOUD_ERROR'); } async _getLoginId(account) { @@ -204,13 +209,13 @@ class MideaCloud { // request cloud.py does NOT pass through _build_request_body). const r = await this._request('/mj/user/login', { data: { - platform: '2', + platform: 2, // BaseCloud.FORMAT (number), cloud.py:283 deviceId: this.deviceId, }, iotData: { appId: this.cfg.appId, src: this.cfg.appId, - clientType: '1', + clientType: 1, // BaseCloud.CLIENT_TYPE (number), cloud.py:289 loginAccount: email, iampwd: this._hashIamPassword(loginId, password), password: this._hashPassword(loginId, password), diff --git a/tests/midea_cloud.test.js b/tests/midea_cloud.test.js index 2ac32382..086057da 100644 --- a/tests/midea_cloud.test.js +++ b/tests/midea_cloud.test.js @@ -34,6 +34,26 @@ test('getToken builds idBytes for both endians without RangeError', async () => assert.equal(calls.length, 2); }); +test('MSmartHome request body sends NUMERIC format/clientType (cloud.py types)', async () => { + const c = new cloud.MideaCloud('msmarthome'); + let sent; + const origFetch = global.fetch; + global.fetch = async (_url, opts) => { sent = JSON.parse(opts.body); return { status: 200, json: async () => ({ code: '0', data: {} }) }; }; + try { + await c._requestMSmart('/v1/user/login/id/get', { loginAccount: 'a@b.de' }); + } finally { global.fetch = origFetch; } + // The Midea cloud rejects "2"/"1" (strings) with "value is illegal" — these must be numbers. + assert.strictEqual(sent.format, 2); + assert.strictEqual(sent.clientType, 1); + assert.equal(typeof sent.format, 'number'); + assert.equal(typeof sent.clientType, 'number'); + // common fields present + passthrough data merged + assert.equal(sent.appId, '1010'); + assert.equal(sent.language, 'en_US'); + assert.equal(sent.loginAccount, 'a@b.de'); + assert.ok(sent.stamp && sent.reqId && sent.deviceId); +}); + test('live login + listDevices', { skip: !process.env.GC_MIDEA_CLOUD }, async () => { const { email, password, app } = JSON.parse(process.env.GC_MIDEA_CLOUD); const c = new cloud.MideaCloud(app);