homebridge-slwf-01pro 0.2.0 → 0.3.0
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 +45 -0
- package/index.js +9 -0
- package/lib/DeviceAccessory.js +6 -1
- package/lib/esphome.js +14 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,51 @@ This package is a maintained fork of [`homebridge-esphome-ac`](https://github.co
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.3.0] — 2026-05-06
|
|
11
|
+
|
|
12
|
+
Final piece of the "fully independent plugin" story: HomeKit UUIDs are now namespaced so this plugin can run **alongside** upstream `homebridge-esphome-ac` (or any other plugin that hashes from the same ESPHome `unique_id`) on the same Homebridge bridge without UUID collisions.
|
|
13
|
+
|
|
14
|
+
### ⚠️ Breaking — accessories will re-pair
|
|
15
|
+
|
|
16
|
+
The UUID input is now `homebridge-slwf-01pro:<deviceId>` instead of bare `<deviceId>`. The same device gets a new UUID under this plugin → Apple Home treats it as a new accessory. **You'll lose room assignments, scenes, and automations** wired to the old accessory tiles. They need to be re-set up in Apple Home after the upgrade.
|
|
17
|
+
|
|
18
|
+
The plugin **automatically evicts** old-schema cached accessories on startup (no manual cleanup required) and registers fresh ones with the new UUIDs.
|
|
19
|
+
|
|
20
|
+
### Why
|
|
21
|
+
|
|
22
|
+
Before 0.3.0, both upstream and this fork derived HomeKit UUIDs from `entity.config.uniqueId` (or its fallback). For a user with both plugins running in the **same** Homebridge bridge (not child-bridged) and both auto-discovering the same physical AC:
|
|
23
|
+
|
|
24
|
+
- Upstream registers UUID `abc-123` for "Air Conditioner"
|
|
25
|
+
- Our fork tries to register UUID `abc-123` too
|
|
26
|
+
- HomeKit rejects the second one (UUID collision within a bridge)
|
|
27
|
+
|
|
28
|
+
Child bridges (which the user is using) insulate against this — each child bridge is its own HomeKit instance — so the bug is theoretical for child-bridge users. But for users who run plugins on the main Homebridge bridge, the fix is mandatory. And it costs nothing to apply universally.
|
|
29
|
+
|
|
30
|
+
After 0.3.0, the two plugins produce **deterministically different UUIDs** for the same device. Both can be installed on the same Homebridge with full auto-discovery enabled and no interaction.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
|
|
34
|
+
- **`accessory.context.schemaVersion`** — stamped on every accessory created or restored. Currently `2` (1 was the unstamped pre-0.3.0 schema). Future schema breakages bump this number.
|
|
35
|
+
- **`evictStaleSchemaAccessories(platform)`** in `lib/esphome.js` — runs first thing in `init()`. Walks `platform.staleAccessories` (populated by `configureAccessory` for entries with the wrong schema version) and unregisters them in one batch via `api.unregisterPlatformAccessories`. Clean cache transition with no manual user action.
|
|
36
|
+
- **`UUID_NAMESPACE = 'homebridge-slwf-01pro'`** constant in `lib/DeviceAccessory.js`. Prefixed onto the device id before hashing.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **HomeKit UUID input format**: `homebridge-slwf-01pro:<deviceId>` (was bare `<deviceId>`). For devices with `unique_id` set in YAML, the old UUID was the same as upstream's; now ours is distinct. For devices without `unique_id` (the SLWF-01Pro/Midea default), the old UUID was already MAC-based via `deriveDeviceId` (added in 0.1.2); the prefix makes it distinct from upstream regardless.
|
|
41
|
+
- **`configureAccessory` in `index.js`** now diverts schema-mismatched cached entries to `this.staleAccessories` and emits a warning. The init flow evicts them before doing anything else.
|
|
42
|
+
|
|
43
|
+
### Migration
|
|
44
|
+
|
|
45
|
+
For anyone on `homebridge-slwf-01pro@0.2.x` or earlier:
|
|
46
|
+
1. `sudo npm install -g homebridge-slwf-01pro@latest`
|
|
47
|
+
2. Restart Homebridge.
|
|
48
|
+
3. The log shows `Evicting N cached accessor… from an older plugin schema. They will be re-registered fresh with stable UUIDs.` Old accessories disappear from Apple Home; new ones appear with the same names but no automations attached.
|
|
49
|
+
4. Re-add the new accessories to your Apple Home rooms / scenes / automations.
|
|
50
|
+
|
|
51
|
+
For anyone migrating from upstream: same as 0.2.0 (config.json edit `"ESPHomeAC"` → `"SLWFOnePro"`), plus the same UUID-driven re-pairing.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
10
55
|
## [0.2.0] — 2026-05-06
|
|
11
56
|
|
|
12
57
|
Clean separation from upstream `homebridge-esphome-ac`. Breaking config change (one-line edit) but eliminates all namespace collision risk.
|
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const ESPHome = require('./lib/esphome');
|
|
|
2
2
|
|
|
3
3
|
const PLUGIN_NAME = 'homebridge-slwf-01pro';
|
|
4
4
|
const PLATFORM_NAME = 'SLWFOnePro';
|
|
5
|
+
const ACCESSORY_SCHEMA_VERSION = 2;
|
|
5
6
|
|
|
6
7
|
class ESPHomeAC {
|
|
7
8
|
constructor(log, config, api) {
|
|
@@ -9,9 +10,11 @@ class ESPHomeAC {
|
|
|
9
10
|
this.log = log;
|
|
10
11
|
|
|
11
12
|
this.accessories = [];
|
|
13
|
+
this.staleAccessories = [];
|
|
12
14
|
this.esphomeDevices = {};
|
|
13
15
|
this.PLUGIN_NAME = PLUGIN_NAME;
|
|
14
16
|
this.PLATFORM_NAME = PLATFORM_NAME;
|
|
17
|
+
this.ACCESSORY_SCHEMA_VERSION = ACCESSORY_SCHEMA_VERSION;
|
|
15
18
|
this.name = config.name || PLATFORM_NAME;
|
|
16
19
|
this.devices = config.devices || [];
|
|
17
20
|
this.debug = config.debug || false;
|
|
@@ -40,6 +43,12 @@ class ESPHomeAC {
|
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
configureAccessory(accessory) {
|
|
46
|
+
const cachedVersion = accessory.context && accessory.context.schemaVersion;
|
|
47
|
+
if (cachedVersion !== ACCESSORY_SCHEMA_VERSION) {
|
|
48
|
+
this.log.warn(`Cached accessory "${accessory.displayName}" is from an older plugin schema (v${cachedVersion || 1}); will be re-registered with the current schema (v${ACCESSORY_SCHEMA_VERSION}).`);
|
|
49
|
+
this.staleAccessories.push(accessory);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
43
52
|
this.log.easyDebug(`Found cached accessory: ${accessory.displayName} (${accessory.context.deviceId || 'no id'})`);
|
|
44
53
|
this.accessories.push(accessory);
|
|
45
54
|
}
|
package/lib/DeviceAccessory.js
CHANGED
|
@@ -26,6 +26,9 @@ let Characteristic;
|
|
|
26
26
|
const SET_DEBOUNCE_MS = 600;
|
|
27
27
|
const PRIMARY_MODES = [ESP_MODE.COOL, ESP_MODE.HEAT, ESP_MODE.AUTO, ESP_MODE.HEAT_COOL];
|
|
28
28
|
|
|
29
|
+
const UUID_NAMESPACE = 'homebridge-slwf-01pro';
|
|
30
|
+
const ACCESSORY_SCHEMA_VERSION = 2;
|
|
31
|
+
|
|
29
32
|
function readSensorValue(entity) {
|
|
30
33
|
if (!entity || !entity.state) return null;
|
|
31
34
|
if (entity.state.missingState) return null;
|
|
@@ -84,12 +87,13 @@ class DeviceAccessory {
|
|
|
84
87
|
|
|
85
88
|
this.swingModeValue = pickDefaultSwingValue(this.config.supportedSwingModesList);
|
|
86
89
|
|
|
87
|
-
this.UUID = this.api.hap.uuid.generate(this.id);
|
|
90
|
+
this.UUID = this.api.hap.uuid.generate(`${UUID_NAMESPACE}:${this.id}`);
|
|
88
91
|
this.accessory = platform.accessories.find(acc => acc.UUID === this.UUID);
|
|
89
92
|
|
|
90
93
|
if (!this.accessory) {
|
|
91
94
|
this.log(`Creating new ESPHome AC accessory: "${this.name}"`);
|
|
92
95
|
this.accessory = new this.api.platformAccessory(this.name, this.UUID);
|
|
96
|
+
this.accessory.context.schemaVersion = ACCESSORY_SCHEMA_VERSION;
|
|
93
97
|
this.accessory.context.deviceId = this.id;
|
|
94
98
|
this.accessory.context.host = this.host;
|
|
95
99
|
this.accessory.context.lastTargetState = chooseInitialTargetMode(this.state.mode);
|
|
@@ -97,6 +101,7 @@ class DeviceAccessory {
|
|
|
97
101
|
this.api.registerPlatformAccessories(platform.PLUGIN_NAME, platform.PLATFORM_NAME, [this.accessory]);
|
|
98
102
|
} else {
|
|
99
103
|
this.log(`ESPHome device "${this.name}" reconnected.`);
|
|
104
|
+
this.accessory.context.schemaVersion = ACCESSORY_SCHEMA_VERSION;
|
|
100
105
|
this.accessory.context.host = this.host;
|
|
101
106
|
if (PRIMARY_MODES.includes(this.state.mode)) {
|
|
102
107
|
this.accessory.context.lastTargetState = this.state.mode;
|
package/lib/esphome.js
CHANGED
|
@@ -60,10 +60,23 @@ function detectOrphanedAccessories(platform) {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
function evictStaleSchemaAccessories(platform) {
|
|
64
|
+
if (!platform.staleAccessories || platform.staleAccessories.length === 0) return;
|
|
65
|
+
const stale = platform.staleAccessories.slice();
|
|
66
|
+
platform.staleAccessories = [];
|
|
67
|
+
platform.log(`Evicting ${stale.length} cached accessor${stale.length === 1 ? 'y' : 'ies'} from an older plugin schema. They will be re-registered fresh with stable UUIDs.`);
|
|
68
|
+
try {
|
|
69
|
+
platform.api.unregisterPlatformAccessories(platform.PLUGIN_NAME, platform.PLATFORM_NAME, stale);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
platform.log.error(`Failed to evict stale-schema accessories: ${err.message || err}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
async function init() {
|
|
64
76
|
const platform = this;
|
|
65
|
-
detectOrphanedAccessories(platform);
|
|
66
77
|
platform._clients = [];
|
|
78
|
+
evictStaleSchemaAccessories(platform);
|
|
79
|
+
detectOrphanedAccessories(platform);
|
|
67
80
|
|
|
68
81
|
const manualDevices = (platform.devices || []).map(d => ({
|
|
69
82
|
...d,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-slwf-01pro",
|
|
3
3
|
"description": "Homebridge plugin for the SMLIGHT SLWF-01Pro Wi-Fi dongle and other ESPHome Climate entities. Auto-discovery, multi-entity bundling, and full Midea-protocol AC support.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/nookied/homebridge-SLWF-01Pro.git"
|