Skip to content

Commit 6760f28

Browse files
Tiwasclaude
andcommitted
fix(circadian): trigger registration, member-action gating, debug logs
- Use getDeviceTriggerCard for clg_target_changed, clg_outdoor_light_requested and clg_error_occurred (they declare a device arg, so getTriggerCard caused "Invalid value for token phase" by treating the device as the tokens object). - Drop the now-unnecessary runListener for clg_outdoor_light_requested (device-trigger filtering is automatic). - clg_turn_on_member now respects CLG onoff/clg_paused: when CLG is inactive it only sets onoff=true on the member instead of pushing circadian values. - Add debug logging across applyCurrentProfile, applyTargetToDevice, save_config and probe_device handlers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e36b055 commit 6760f28

2 files changed

Lines changed: 49 additions & 19 deletions

File tree

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

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ class CircadianLightGroupDevice extends Homey.Device {
227227
if (this.deleted) return false;
228228

229229
const config = this.getConfig();
230-
const devices = Array.isArray(config.devices) ? config.devices.filter(device => device.enabled !== false) : [];
230+
const allDevices = Array.isArray(config.devices) ? config.devices : [];
231+
const devices = allDevices.filter(device => device.enabled !== false);
232+
this.debug(`apply[${reason}] config: ${allDevices.length} device(s) total, ${devices.length} enabled`);
231233

232234
let outdoor;
233235
try {
@@ -245,6 +247,7 @@ class CircadianLightGroupDevice extends Homey.Device {
245247
});
246248

247249
this.applyOverridesToTarget(target);
250+
this.debug(`apply[${reason}] target: phase=${target.phase} dim=${target.dim?.toFixed?.(3)} temp=${target.temperature?.toFixed?.(3)} mode=${target.mode} outdoorLux=${outdoor.outdoorComputedLux} (source=${outdoor.source})`);
248251

249252
// Phase + red mode change detection.
250253
if (this.previousPhase !== null && this.previousPhase !== target.phase) {
@@ -273,10 +276,12 @@ class CircadianLightGroupDevice extends Homey.Device {
273276
&& this.getCapabilityValue('clg_paused') !== true;
274277

275278
if (!shouldApplyToLights) {
279+
this.debug(`apply[${reason}] SKIPPED push to lights: onoff=${this.getCapabilityValue('onoff')} clg_paused=${this.getCapabilityValue('clg_paused')}`);
276280
return false;
277281
}
278282

279283
if (devices.length === 0) {
284+
this.debug(`apply[${reason}] SKIPPED push to lights: no enabled devices in config`);
280285
await this.setCapabilityValue('alarm_config', true).catch(this.error);
281286
return false;
282287
}
@@ -299,7 +304,7 @@ class CircadianLightGroupDevice extends Homey.Device {
299304
await this.triggerError(`${errors.length} light(s) failed during ${reason}`);
300305
}
301306

302-
await this.homey.flow.getTriggerCard('clg_target_changed')
307+
await this.homey.flow.getDeviceTriggerCard('clg_target_changed')
303308
.trigger(this, {
304309
phase: target.phase,
305310
dim: target.dim,
@@ -314,24 +319,36 @@ class CircadianLightGroupDevice extends Homey.Device {
314319
async requestExternalOutdoorLightIfNeeded(config) {
315320
if (config.outdoorLight?.provider !== 'external_value') return;
316321

317-
await this.homey.flow.getTriggerCard('clg_outdoor_light_requested')
318-
.trigger(this, {}, { deviceId: this.getData().id })
322+
await this.homey.flow.getDeviceTriggerCard('clg_outdoor_light_requested')
323+
.trigger(this, {}, {})
319324
.catch(this.error);
320325
}
321326

322327
async applyTargetToDevice(item, target) {
323-
if (!item.id) return;
328+
if (!item.id) {
329+
this.debug(`applyTargetToDevice: skipped (no id) for ${item.name || '<unnamed>'}`);
330+
return;
331+
}
324332

333+
const label = item.name || item.id;
325334
const apiDevice = await this.homey.app.api.devices.getDevice({ id: item.id });
326335
const caps = apiDevice.capabilitiesObj || {};
327336
const isOn = caps.onoff?.value === true;
328337
const unsafePrewarmDevices = await this.getStoreValue('unsafe_prewarm_devices') || {};
329338
const prewarmBeforeOn = item.prewarmBeforeOn !== false && !unsafePrewarmDevices[item.id];
330339

331-
if (!isOn && !prewarmBeforeOn) return;
340+
if (!isOn && !prewarmBeforeOn) {
341+
this.debug(`applyTargetToDevice[${label}]: skipped (off + prewarm disabled)`);
342+
return;
343+
}
332344

333345
const capabilitiesToSet = this.getCapabilitiesToSet(item, target, caps, isOn);
334-
if (capabilitiesToSet.length === 0) return;
346+
if (capabilitiesToSet.length === 0) {
347+
this.debug(`applyTargetToDevice[${label}]: nothing to set (isOn=${isOn})`);
348+
return;
349+
}
350+
351+
this.debug(`applyTargetToDevice[${label}]: isOn=${isOn} writes=${JSON.stringify(capabilitiesToSet)}`);
335352

336353
for (const [capability, value] of capabilitiesToSet) {
337354
await apiDevice.setCapabilityValue(capability, value);
@@ -386,7 +403,7 @@ class CircadianLightGroupDevice extends Homey.Device {
386403

387404
async triggerError(error) {
388405
if (this.getSetting('log_errors') !== false) {
389-
await this.homey.flow.getTriggerCard('clg_error_occurred')
406+
await this.homey.flow.getDeviceTriggerCard('clg_error_occurred')
390407
.trigger(this, { error })
391408
.catch(this.error);
392409
}
@@ -499,6 +516,16 @@ class CircadianLightGroupDevice extends Homey.Device {
499516
const item = (Array.isArray(config.devices) ? config.devices : []).find(d => d.id === memberId);
500517
if (!item) throw new Error('Light is not a member of this group');
501518

519+
const clgActive = this.getCapabilityValue('onoff') === true && this.getCapabilityValue('clg_paused') !== true;
520+
if (!clgActive) {
521+
const apiDevice = await this.homey.app.api.devices.getDevice({ id: item.id });
522+
if (apiDevice.capabilitiesObj?.onoff?.setable && apiDevice.capabilitiesObj.onoff.value !== true) {
523+
await apiDevice.setCapabilityValue('onoff', true);
524+
}
525+
this.debug(`turn_on_member[${item.name || item.id}]: CLG inactive, only set onoff=true`);
526+
return true;
527+
}
528+
502529
let outdoor;
503530
try {
504531
outdoor = await this.outdoorProvider.getOutdoorLight(config.outdoorLight || {});

no.tiwas.booleantoolbox/drivers/circadian-light-group/driver.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,6 @@ class CircadianLightGroupDriver extends Homey.Driver {
174174
cond('clg_red_mode_active', 'onConditionRedModeActive');
175175
cond('clg_is_paused', 'onConditionIsPaused');
176176
cond('clg_is_on', 'onConditionIsOn');
177-
178-
this.homey.flow.getTriggerCard('clg_outdoor_light_requested')
179-
.registerRunListener(async (args, state) => args.device?.getData().id === state?.deviceId);
180177
}
181178

182179
async probeDeviceAsync(deviceId, session) {
@@ -468,23 +465,29 @@ class CircadianLightGroupDriver extends Homey.Driver {
468465
}));
469466

470467
session.setHandler('save_config', async (data) => {
471-
const config = data?.config || {};
472-
const configJson = JSON.stringify(config, null, 2);
473-
474-
JSON.parse(configJson);
475-
await device.setSettings({ config_json: configJson });
476-
// setSettings triggers onSettings on the device, which re-runs lux watchers
477-
// and the scheduler.
478-
return { success: true };
468+
this.debug(`save_config invoked, devices=${(data?.config?.devices || []).length}`);
469+
try {
470+
const config = data?.config || {};
471+
const configJson = JSON.stringify(config, null, 2);
472+
JSON.parse(configJson);
473+
await device.setSettings({ config_json: configJson });
474+
this.debug('save_config OK');
475+
return { success: true };
476+
} catch (error) {
477+
this.error('save_config failed:', error);
478+
throw error;
479+
}
479480
});
480481

481482
session.setHandler('get_light_candidates', async () => this.getLightCandidates({ wholeHouse: true }));
482483
session.setHandler('get_lux_sensors', async () => this.getLuxSensors());
483484

484485
session.setHandler('probe_device', async ({ deviceId } = {}) => {
486+
this.debug(`probe_device invoked deviceId=${deviceId}`);
485487
if (!deviceId) throw new Error('deviceId required');
486488
this.probeDeviceAsync(deviceId, session)
487489
.catch(error => {
490+
this.error(`probe_device async failure for ${deviceId}:`, error);
488491
session.emit('probe_complete', { deviceId, tested: false, error: error.message, support: {} }).catch(() => {});
489492
});
490493
return { started: true };

0 commit comments

Comments
 (0)