homebridge-tuya-plus 3.14.0-dev.45 → 3.14.0-dev.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/TuyaCloudDevice.js +18 -1
- package/package.json +1 -1
- package/test/TuyaCloudDevice.test.js +33 -0
package/lib/TuyaCloudDevice.js
CHANGED
|
@@ -310,9 +310,26 @@ class TuyaCloudDevice extends EventEmitter {
|
|
|
310
310
|
_propsToState(props) {
|
|
311
311
|
const state = {};
|
|
312
312
|
(props || []).forEach(item => {
|
|
313
|
-
if (item
|
|
313
|
+
if (!item || typeof item !== 'object') return;
|
|
314
|
+
if (typeof item.code === 'string') {
|
|
314
315
|
this._indexInto(state, item.code, item.value, item.dp_id != null ? item.dp_id : item.dpId);
|
|
316
|
+
return;
|
|
315
317
|
}
|
|
318
|
+
// A realtime item with no `code`: Tuya's MQTT stream delivers some
|
|
319
|
+
// data-points as a bare { "<dpId>": value } pair — no `code`, no `value`
|
|
320
|
+
// field. Custom / non-standard DPs come through this way (e.g. an
|
|
321
|
+
// irrigation timer's `charging`, dp 101, which lives only in the
|
|
322
|
+
// thing-model shadow, not the standard instruction set, so the message
|
|
323
|
+
// service never attaches a code). Map the numeric id back to its code,
|
|
324
|
+
// learned from the shadow on connect, so the change is applied instead of
|
|
325
|
+
// silently dropped; a still-unmapped id is indexed numerically, which a
|
|
326
|
+
// LAN-style config resolves.
|
|
327
|
+
Object.keys(item).forEach(key => {
|
|
328
|
+
if (!/^\d+$/.test(key)) return;
|
|
329
|
+
const code = this.codeByDpId[key];
|
|
330
|
+
if (code != null) this._indexInto(state, code, item[key], key);
|
|
331
|
+
else state[key] = item[key];
|
|
332
|
+
});
|
|
316
333
|
});
|
|
317
334
|
return state;
|
|
318
335
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-tuya-plus",
|
|
3
|
-
"version": "3.14.0-dev.
|
|
3
|
+
"version": "3.14.0-dev.46",
|
|
4
4
|
"description": "A community-maintained Homebridge plugin for controlling Tuya devices in HomeKit. LAN-first, with an optional Tuya Cloud fallback for any device the LAN can't reach.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -279,6 +279,39 @@ describe('TuyaCloudDevice', () => {
|
|
|
279
279
|
expect(changes[0]).toEqual({switch_1: true, '1': true});
|
|
280
280
|
});
|
|
281
281
|
|
|
282
|
+
test('realtime bare numeric-keyed items (no code) are mapped to their code', async () => {
|
|
283
|
+
// Custom/non-standard DPs (e.g. an irrigation timer's `charging`, dp 101)
|
|
284
|
+
// arrive over MQTT as a bare { "<dpId>": value } pair with no `code` field.
|
|
285
|
+
// They must be applied via the learned dp→code map, not dropped.
|
|
286
|
+
const api = makeApi();
|
|
287
|
+
api.getShadowProperties = jest.fn().mockResolvedValue([
|
|
288
|
+
{code: 'switch_1', dp_id: 1, value: false},
|
|
289
|
+
{code: 'charging', dp_id: 101, value: false}
|
|
290
|
+
]);
|
|
291
|
+
const dev = makeDevice(api);
|
|
292
|
+
await dev._connect();
|
|
293
|
+
|
|
294
|
+
const changes = [];
|
|
295
|
+
dev.on('change', c => changes.push(c));
|
|
296
|
+
dev._applyStatus([{'101': true}]); // bare numeric, as the message service delivers `charging`
|
|
297
|
+
expect(dev.state.charging).toBe(true);
|
|
298
|
+
expect(dev.state['101']).toBe(true);
|
|
299
|
+
expect(changes[0]).toEqual({charging: true, '101': true});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('a bare numeric item with no learned code is still indexed numerically', async () => {
|
|
303
|
+
const api = makeApi();
|
|
304
|
+
api.getShadowProperties = jest.fn().mockResolvedValue([{code: 'switch_1', dp_id: 1, value: false}]);
|
|
305
|
+
const dev = makeDevice(api);
|
|
306
|
+
await dev._connect();
|
|
307
|
+
|
|
308
|
+
const changes = [];
|
|
309
|
+
dev.on('change', c => changes.push(c));
|
|
310
|
+
dev._applyStatus([{'137': 42}]); // unknown dp, no code mapping
|
|
311
|
+
expect(dev.state['137']).toBe(42);
|
|
312
|
+
expect(changes[0]).toEqual({'137': 42});
|
|
313
|
+
});
|
|
314
|
+
|
|
282
315
|
test('falls back to /status (code-only) when the shadow is unavailable', async () => {
|
|
283
316
|
const api = makeApi();
|
|
284
317
|
api.getShadowProperties = jest.fn().mockResolvedValue(null);
|