diff --git a/server/src/services/greenModeService/index.js b/server/src/services/greenModeService/index.js index f473572c..4effbbaf 100644 --- a/server/src/services/greenModeService/index.js +++ b/server/src/services/greenModeService/index.js @@ -19,7 +19,7 @@ async function setEnabled(nextValue, options = {}) { const next = Boolean(nextValue); if (enabled === next) return enabled; - if (next) { + if (next && homeAssistantService.enabled) { /* Lock first because the existing locked-on transition sets lights white. Recoloring RGB lights afterward leaves them green while retaining the @@ -54,7 +54,7 @@ async function setEnabled(nextValue, options = {}) { if (failures.length) { logger.warn('Some room controls failed to enter green mode', { failures }); } - } else { + } else if (!next && homeAssistantService.enabled) { // Disabling the visual mode simply releases the lock it created. Bulb // colors remain untouched, matching the existing one-shot light behavior. await homeAssistantService.setLightsLockedOn(false, { @@ -62,6 +62,14 @@ async function setEnabled(nextValue, options = {}) { }); } + /* + Home Assistant is deliberately optional here. When it is not configured, + skipping the physical-room operations still allows the session theme, + CardFrame styling, alerts, commands, and timed reward to work normally. + The integration's generic lock state is also left untouched because there + are no server-managed room controls to lock. + */ + enabled = next; logger.info('Green mode changed', { enabled, diff --git a/server/src/services/operatorCommandService/index.test.js b/server/src/services/operatorCommandService/index.test.js index f6dd8785..e5714ab2 100644 --- a/server/src/services/operatorCommandService/index.test.js +++ b/server/src/services/operatorCommandService/index.test.js @@ -130,6 +130,11 @@ test('a disabled required feature is reported before any permission check', asyn assert.match(await run('rs lights on', nonAdmin), /Home Assistant feature is not configured/); }); +test('green mode remains available without optional Home Assistant features', async () => { + const run = createRouter({ featureEnabled: false }); + assert.match(await run('rs green on', admin), /Green mode enabled/); +}); + test('ordinary words that merely start with the prefix are not commands', async () => { const run = createRouter(); assert.equal(await run('rsvp', nonAdmin), ''); diff --git a/server/src/services/operatorCommandService/registry.js b/server/src/services/operatorCommandService/registry.js index ecc5efa1..ba5ea416 100644 --- a/server/src/services/operatorCommandService/registry.js +++ b/server/src/services/operatorCommandService/registry.js @@ -19,7 +19,10 @@ function buildCommandRegistry(prefix, timeCommand) { mode: { category: 'admin', summary: 'Change the server mode.', usage: [`${prefix} mode `], access: 'Admin', permission: 'admin' }, reason: { category: 'admin', summary: 'Show, set, or clear the admin-mode reason.', usage: [`${prefix} reason [text|clear]`], access: 'Admin to change' }, goal: { category: 'admin', summary: 'Show, set, or clear the global objective.', usage: [`${prefix} goal [text|clear]`], access: 'Admin to change' }, - green: { category: 'admin', summary: 'Toggle green room and page mode.', usage: [`${prefix} green `], access: 'Admin', permission: 'admin', requiredFeature: 'homeAssistant', unavailableLabel: 'Home Assistant' }, + // Green mode is fundamentally a server theme. Home Assistant lighting is + // an optional enhancement, so the command must remain available when that + // integration is absent. + green: { category: 'admin', summary: 'Toggle green room and page mode.', usage: [`${prefix} green `], access: 'Admin', permission: 'admin' }, lights: { category: 'features', summary: 'Control room lights or manage the admin light lock.',