homebridge-xiaomi-miot 1.2.0 → 1.2.2
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/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.2] - 2026-11-09
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Offline detection no longer interferes with the MiOT library's reconnect cycle** — `pushOffline` previously set `miotDevice.localConnected = false` directly, which caused the vendored library to treat the device as disconnected and trigger an unnecessary handshake/reconnect cycle (~80 s downtime) even when the device was actually reachable. Now `localConnected` is left untouched; instead, `onSet` handlers are patched (the same way `onGet` handlers already were) so that both reads and writes correctly return `SERVICE_COMMUNICATION_FAILURE` while offline. Handlers are restored when the device comes back.
|
|
7
|
+
- **Property set and action failures are now logged at `warn` level** — errors in `setProperty` and `executeAction` were previously swallowed into `debug`-only logs, making silent control failures invisible to users. They now appear as `warn` messages in the Homebridge log.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## [1.2.1] - 2026-06-08
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Removed all references to the previously used third-party MiOT library from comments and documentation. The plugin is now fully independent with no external plugin dependencies.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
3
18
|
## [1.2.0] - 2026-06-08
|
|
4
19
|
|
|
5
20
|
### Changed
|
|
6
|
-
- **Zero external plugin dependencies** —
|
|
7
|
-
- Sub-dependencies
|
|
21
|
+
- **Zero external plugin dependencies** — MiOT protocol library (343 files: protocol, device modules, factories, constants, utils, wrappers) is now bundled directly inside the plugin under `vendor/miot/`. The plugin is fully self-contained.
|
|
22
|
+
- Sub-dependencies (`color-convert`, `env-paths`, `randomstring`) are now declared explicitly as direct dependencies.
|
|
8
23
|
|
|
9
24
|
---
|
|
10
25
|
|
|
@@ -18,7 +33,7 @@
|
|
|
18
33
|
## [1.1.2] - 2026-06-08
|
|
19
34
|
|
|
20
35
|
### Changed
|
|
21
|
-
- **Minimalist logging** — startup output reduced to a single `Starting N device(s)…` summary line. All verbose
|
|
36
|
+
- **Minimalist logging** — startup output reduced to a single `Starting N device(s)…` summary line. All verbose MiOT internal messages (device properties, actions, services, accessory init, filter stats, model detection, connection details) are now suppressed. Only warnings, errors, and offline/online state changes remain visible.
|
|
22
37
|
|
|
23
38
|
---
|
|
24
39
|
|
|
@@ -27,8 +42,8 @@
|
|
|
27
42
|
### Added
|
|
28
43
|
- **Offline detection** — devices that lose network connectivity now show "No Response" in HomeKit. Detection works by overriding HAP `getHandler`s to throw `SERVICE_COMMUNICATION_FAILURE` after 4 missed polling cycles. Original handlers are restored automatically when the device reconnects.
|
|
29
44
|
- **Auto/sleep mode rotation speed** — when a device is in Auto or Sleep mode, the `RotationSpeed` characteristic shows a configurable placeholder value (default `50%`) instead of `0%`. Configurable via `"autoRotationSpeed"` in device config (set to `0` to disable).
|
|
30
|
-
- **Per-model feature toggles (Config UI)** — the device settings UI now lists device-specific feature toggles (buzzer, LED, child lock, modes, fan speed, swing, screen, ioniser, etc.) sourced from the
|
|
31
|
-
- **`normalizeFeatures()`** — on every UI render, any feature key that is `undefined` in config is explicitly written as `false` before saving, preventing
|
|
45
|
+
- **Per-model feature toggles (Config UI)** — the device settings UI now lists device-specific feature toggles (buzzer, LED, child lock, modes, fan speed, swing, screen, ioniser, etc.) sourced from the bundled device registry. Features default to **OFF**; the user enables only what they need. Covers 83+ device models across 7 categories (fans, air purifiers, humidifiers, lights, heaters, vacuums, outlets).
|
|
46
|
+
- **`normalizeFeatures()`** — on every UI render, any feature key that is `undefined` in config is explicitly written as `false` before saving, preventing built-in defaults from silently enabling disabled features.
|
|
32
47
|
- **`device-features.js`** — feature definitions moved to a separate file for easy maintenance; adding a new model requires editing only that file.
|
|
33
48
|
- **`autoRotationSpeed` config key** in `config.schema.json`.
|
|
34
49
|
|
|
@@ -56,6 +71,6 @@
|
|
|
56
71
|
|
|
57
72
|
### Added
|
|
58
73
|
- Initial release.
|
|
59
|
-
-
|
|
74
|
+
- Homebridge platform plugin for Xiaomi MiOT devices.
|
|
60
75
|
- Config UI with per-device settings (name, IP, token, model, polling interval, connection type).
|
|
61
76
|
- Cloud (MiCloud) and local connection modes.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* screenControl, showTemperature,
|
|
11
11
|
* buzzerControl, ledControl, childLockControl, offDelayControl
|
|
12
12
|
*
|
|
13
|
-
* Mənbə:
|
|
13
|
+
* Mənbə: vendor/miot/lib/modules/{kategoria}/devices/
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/* global DEVICE_FEATURES */
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
/* ── Render ── */
|
|
233
|
-
/* Hər cihazın feature key-lərini açıqca false ilə başlat (undefined →
|
|
233
|
+
/* Hər cihazın feature key-lərini açıqca false ilə başlat (undefined → default olaraq true sayılır) */
|
|
234
234
|
function normalizeFeatures() {
|
|
235
235
|
let changed = false;
|
|
236
236
|
(config.devices || []).forEach(d => {
|
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs').promises;
|
|
4
4
|
const path = require('path');
|
|
5
|
-
//
|
|
5
|
+
// Bundled MiOT library
|
|
6
6
|
const MIOT_ROOT = path.join(__dirname, 'vendor/miot/lib') + '/';
|
|
7
7
|
const MiotDevice = require(MIOT_ROOT + 'protocol/MiotDevice.js');
|
|
8
8
|
const DeviceFactory = require(MIOT_ROOT + 'factories/DeviceFactory.js');
|
|
@@ -12,9 +12,9 @@ const Events = require(MIOT_ROOT + 'constants/Events.js');
|
|
|
12
12
|
|
|
13
13
|
const PLUGIN_NAME = 'homebridge-xiaomi-miot';
|
|
14
14
|
const PLATFORM_NAME = 'XiaomiMiot';
|
|
15
|
-
const PLUGIN_VERSION = '1.2.
|
|
15
|
+
const PLUGIN_VERSION = '1.2.2';
|
|
16
16
|
|
|
17
|
-
/* ── Silence
|
|
17
|
+
/* ── Silence verbose MiOT startup logs ── */
|
|
18
18
|
const MIOT_INFO_SUPPRESS = [
|
|
19
19
|
'Initializing device services', 'Device services:',
|
|
20
20
|
'Initializing device properties', 'Device properties:',
|
|
@@ -229,7 +229,6 @@ class XiaomiMiotDeviceController {
|
|
|
229
229
|
|
|
230
230
|
/* ── Install error-throwing getHandler on every characteristic ── */
|
|
231
231
|
const pushOffline = () => {
|
|
232
|
-
this.miotDevice.localConnected = false;
|
|
233
232
|
eachChar(char => {
|
|
234
233
|
/* Save original handler (once only) and replace with error thrower */
|
|
235
234
|
if (!Object.prototype.hasOwnProperty.call(char, '_xiaomiOrigGet')) {
|
|
@@ -238,13 +237,18 @@ class XiaomiMiotDeviceController {
|
|
|
238
237
|
throw new HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
|
|
239
238
|
});
|
|
240
239
|
}
|
|
240
|
+
if (!Object.prototype.hasOwnProperty.call(char, '_xiaomiOrigSet') && char.setHandler !== undefined) {
|
|
241
|
+
char._xiaomiOrigSet = char.setHandler;
|
|
242
|
+
char.onSet(() => {
|
|
243
|
+
throw new HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
241
246
|
});
|
|
242
247
|
this.logger.warn(`[OfflineDetect] ${this.name} — offline.`);
|
|
243
248
|
};
|
|
244
249
|
|
|
245
|
-
/* ── Restore original
|
|
250
|
+
/* ── Restore original get/set handlers when device comes back ── */
|
|
246
251
|
const pushOnline = () => {
|
|
247
|
-
this.miotDevice.localConnected = true;
|
|
248
252
|
eachChar(char => {
|
|
249
253
|
if (Object.prototype.hasOwnProperty.call(char, '_xiaomiOrigGet')) {
|
|
250
254
|
if (char._xiaomiOrigGet) {
|
|
@@ -254,6 +258,10 @@ class XiaomiMiotDeviceController {
|
|
|
254
258
|
}
|
|
255
259
|
delete char._xiaomiOrigGet;
|
|
256
260
|
}
|
|
261
|
+
if (Object.prototype.hasOwnProperty.call(char, '_xiaomiOrigSet')) {
|
|
262
|
+
char.onSet(char._xiaomiOrigSet);
|
|
263
|
+
delete char._xiaomiOrigSet;
|
|
264
|
+
}
|
|
257
265
|
});
|
|
258
266
|
};
|
|
259
267
|
|
|
@@ -312,7 +320,7 @@ class XiaomiMiotDeviceController {
|
|
|
312
320
|
return val;
|
|
313
321
|
});
|
|
314
322
|
|
|
315
|
-
/* 2) Fix push updates —
|
|
323
|
+
/* 2) Fix push updates — the MiOT layer calls updateValue(0) after each poll.
|
|
316
324
|
Our listener runs after it (registered later) and corrects the value. */
|
|
317
325
|
this.miotDevice.on(Events.MIOT_DEVICE_ALL_PROPERTIES_UPDATED, () => {
|
|
318
326
|
if (rsChar && this._isInAutoOrSleepMode(dev)) {
|
package/package.json
CHANGED
package/src/micloud.js
CHANGED
|
@@ -208,7 +208,7 @@ class MiCloud {
|
|
|
208
208
|
this.log.info(`[MiCloud] Login successful — ssecurity=${!!this.ssecurity} serviceToken=${!!this.serviceToken}`);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
/* ── Get device list — RC4 encrypted
|
|
211
|
+
/* ── Get device list — RC4 encrypted ── */
|
|
212
212
|
async getDevices() {
|
|
213
213
|
if (!this.serviceToken) await this.login();
|
|
214
214
|
|
|
@@ -247,12 +247,12 @@ class MiCloud {
|
|
|
247
247
|
let parsed = null;
|
|
248
248
|
|
|
249
249
|
if (this.ssecurity) {
|
|
250
|
-
// ── RC4 encrypted path
|
|
250
|
+
// ── RC4 encrypted path ──────────────────────────────────────────────
|
|
251
251
|
const params = { data: JSON.stringify(dataObj) };
|
|
252
252
|
const nonce = genNonce();
|
|
253
253
|
const sNonce = signedNonce(this.ssecurity, nonce);
|
|
254
254
|
|
|
255
|
-
// Build RC4 body
|
|
255
|
+
// Build RC4 body
|
|
256
256
|
params['rc4_hash__'] = sha1Sign('POST', apiUrl, params, sNonce);
|
|
257
257
|
const encParams = {};
|
|
258
258
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -1237,7 +1237,7 @@ class MiotDevice extends EventEmitter {
|
|
|
1237
1237
|
throw new Error(`Error while setting property ${prop.getName()} to value ${value}! Invalid response. Response: ${JSON.stringify(result)}`);
|
|
1238
1238
|
}
|
|
1239
1239
|
} catch (err) {
|
|
1240
|
-
this.logger.
|
|
1240
|
+
this.logger.warn(`Set property failed: ${err.message || err}`);
|
|
1241
1241
|
}
|
|
1242
1242
|
}
|
|
1243
1243
|
|
|
@@ -1262,7 +1262,7 @@ class MiotDevice extends EventEmitter {
|
|
|
1262
1262
|
throw new Error(`Error while executing action ${action.getName()} with params ${paramValues}! Invalid response from device. Response: ${JSON.stringify(result)}`);
|
|
1263
1263
|
}
|
|
1264
1264
|
} catch (err) {
|
|
1265
|
-
this.logger.
|
|
1265
|
+
this.logger.warn(`Execute action failed: ${err.message || err}`);
|
|
1266
1266
|
}
|
|
1267
1267
|
}
|
|
1268
1268
|
|