Skip to content

Commit ef8709f

Browse files
Tiwasclaude
andcommitted
fix(clg-collection): resolve UUID members via management API
device.id on local Homey.Device instances doesn't reliably expose the Homey-wide UUID under SDK 3, so the previous fallback never matched configs whose member ids were stored as UUIDs. resolveMemberEntries is now async and uses api.devices.getDevices() to translate any UUID in the config to its data.id before mapping to the local device instance. This unblocks pre-existing Collections (incl. the one in this repo) without requiring re-pairing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 394658a commit ef8709f

2 files changed

Lines changed: 52 additions & 15 deletions

File tree

no.tiwas.booleantoolbox/.homeychangelog.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,8 @@
8282
},
8383
"1.10.11": {
8484
"en": "Fix: Circadian Light Group Collection now correctly forwards pause/resume/turn-on/apply and other flow actions to each member group."
85+
},
86+
"1.10.12": {
87+
"en": "Fix: Circadian Light Group Collection now resolves existing UUID-based members via Homey API, so toggling the Collection actually turns each member group on/off."
8588
}
8689
}

no.tiwas.booleantoolbox/drivers/circadian-light-group-collection/device.js

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,59 @@ class CircadianLightGroupCollectionDevice extends CircadianLightGroupDevice {
2323
return driver && typeof driver.getDevices === 'function' ? driver.getDevices() : [];
2424
}
2525

26-
resolveMemberEntries() {
26+
async resolveMemberEntries() {
2727
const byDataId = new Map();
28-
const byHomeyId = new Map();
2928
this.getAvailableCircadianGroups().forEach(device => {
3029
const dataId = device.getData?.().id;
3130
if (dataId) byDataId.set(dataId, device);
32-
// device.id is the Homey-wide UUID exposed on local device instances
33-
const homeyId = device.id;
34-
if (homeyId) byHomeyId.set(homeyId, device);
3531
});
3632

37-
return this.getMemberItems().map(item => ({
38-
id: item.id,
39-
name: item.name || item.id,
40-
item,
41-
memberDevice: byDataId.get(item.id) || byHomeyId.get(item.id) || null,
42-
}));
33+
const items = this.getMemberItems();
34+
const unresolved = items.filter(item => !byDataId.has(item.id));
35+
36+
let uuidToDataId = null;
37+
if (unresolved.length > 0) {
38+
try {
39+
const api = this.homey.app && this.homey.app.api;
40+
if (api) {
41+
const all = await api.devices.getDevices();
42+
uuidToDataId = new Map();
43+
Object.values(all).forEach(d => {
44+
const driverRef = `${d.driverUri || ''}|${d.driverId || ''}|${d.driver?.id || ''}`;
45+
if (!driverRef.includes('circadian-light-group')) return;
46+
if (driverRef.includes('circadian-light-group-collection')) return;
47+
const dataId = d.data?.id;
48+
if (d.id && dataId) uuidToDataId.set(d.id, dataId);
49+
});
50+
}
51+
} catch (error) {
52+
this.debug(`resolveMemberEntries: API lookup failed: ${error.message}`);
53+
}
54+
}
55+
56+
const entries = items.map(item => {
57+
let memberDevice = byDataId.get(item.id) || null;
58+
if (!memberDevice && uuidToDataId) {
59+
const dataId = uuidToDataId.get(item.id);
60+
if (dataId) memberDevice = byDataId.get(dataId) || null;
61+
}
62+
return {
63+
id: item.id,
64+
name: item.name || item.id,
65+
item,
66+
memberDevice,
67+
};
68+
});
69+
70+
const missing = entries.filter(e => !e.memberDevice).map(e => e.name || e.id);
71+
if (missing.length > 0) {
72+
this.debug(`resolveMemberEntries: ${missing.length} member(s) not found locally: ${missing.join(', ')}`);
73+
}
74+
return entries;
4375
}
4476

4577
async runForMemberGroups(label, taskFn, verifyFn = null) {
46-
const entries = this.resolveMemberEntries();
78+
const entries = await this.resolveMemberEntries();
4779
if (entries.length === 0) {
4880
this.debug(`${label}: no Circadian Light Group members configured`);
4981
return { ok: [], failed: [] };
@@ -122,7 +154,8 @@ class CircadianLightGroupCollectionDevice extends CircadianLightGroupDevice {
122154
const memberId = args.member?.id;
123155
if (!memberId) throw new Error('No Circadian Light Group selected');
124156

125-
const entry = this.resolveMemberEntries().find(candidate => candidate.id === memberId);
157+
const entries = await this.resolveMemberEntries();
158+
const entry = entries.find(candidate => candidate.id === memberId);
126159
if (!entry || !entry.memberDevice) throw new Error('Circadian Light Group member not found');
127160

128161
await entry.memberDevice.onFlowTurnOn();
@@ -188,13 +221,14 @@ class CircadianLightGroupCollectionDevice extends CircadianLightGroupDevice {
188221
}
189222

190223
async onConditionIsInPhase(args) {
191-
const entries = this.resolveMemberEntries().filter(entry => entry.memberDevice);
224+
const entries = (await this.resolveMemberEntries()).filter(entry => entry.memberDevice);
192225
if (entries.length === 0) return false;
193226
return entries.every(entry => entry.memberDevice.previousPhase === args.phase);
194227
}
195228

196229
async onConditionRedModeActive() {
197-
return this.resolveMemberEntries()
230+
const entries = await this.resolveMemberEntries();
231+
return entries
198232
.filter(entry => entry.memberDevice)
199233
.some(entry => entry.memberDevice.previousRedMode === true);
200234
}

0 commit comments

Comments
 (0)