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** — `homebridge-miot` dependency removed. The 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.
7
- - Sub-dependencies previously pulled in transitively via `homebridge-miot` (`color-convert`, `env-paths`, `randomstring`) are now declared explicitly as direct 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 homebridge-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.
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 homebridge-miot 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).
31
- - **`normalizeFeatures()`** — on every UI render, any feature key that is `undefined` in config is explicitly written as `false` before saving, preventing homebridge-miot's built-in defaults from silently enabling disabled features.
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
- - Wraps `homebridge-miot` as a child-bridge platform plugin.
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ə: https://github.com/merdok/homebridge-miot — lib/modules/{kategoria}/devices/
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 → homebridge-miot true sayır) */
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
- // Vendored MiOT library (bundled — no external homebridge-miot dependency)
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.0';
15
+ const PLUGIN_VERSION = '1.2.2';
16
16
 
17
- /* ── Silence homebridge-miot's verbose startup logs ── */
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 getHandlers when device comes back ── */
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 — homebridge-miot calls updateValue(0) after each poll.
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "homebridge-xiaomi-miot",
3
3
  "displayName": "Homebridge Xiaomi MiOT",
4
- "version": "1.2.0",
4
+ "version": "1.2.2",
5
5
  "description": "Homebridge plugin for Xiaomi devices using the MiOT protocol — local and MiCloud",
6
6
  "main": "index.js",
7
7
  "scripts": {},
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 (matches homebridge-miot default) ── */
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 (homebridge-miot default) ──────────────────
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 identical to homebridge-miot _generateRc4Body
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.debug(err);
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.debug(err);
1265
+ this.logger.warn(`Execute action failed: ${err.message || err}`);
1266
1266
  }
1267
1267
  }
1268
1268