mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
home assistant always turn lights on at full brightness trying to fix weird bulb
This commit is contained in:
@@ -80,6 +80,27 @@ function normalizeRgbColor(color) {
|
||||
function createRuntimeEngine(deps) {
|
||||
const { logger, enabled, haConfig, callHomeAssistantService } = deps;
|
||||
|
||||
async function turnOnLightAtFullBrightness(entityId, serviceData = {}) {
|
||||
/*
|
||||
Every server-owned interaction that turns on or changes a light must
|
||||
also restore it to full brightness. Home Assistant remembers a bulb's
|
||||
previous brightness, so sending only a color or color temperature can
|
||||
otherwise make a light appear unexpectedly dim even though this service
|
||||
requested an on-state.
|
||||
|
||||
Keeping this rule in one helper makes it apply consistently to ordinary
|
||||
on commands, RGB changes, white-temperature changes, bulk operations,
|
||||
random scenes, and lock-on behavior. brightness_pct is deliberately
|
||||
written after the caller's service data so future call sites cannot
|
||||
accidentally override the service-wide 100 percent requirement.
|
||||
*/
|
||||
await callHomeAssistantService('light', 'turn_on', {
|
||||
entity_id: entityId,
|
||||
...serviceData,
|
||||
brightness_pct: 100,
|
||||
});
|
||||
}
|
||||
|
||||
function emitUpdate(getState) {
|
||||
events.emit('update', getState());
|
||||
}
|
||||
@@ -143,7 +164,14 @@ function createRuntimeEngine(deps) {
|
||||
const domain = String(meta.domain || (meta.type === 'light' ? 'light' : 'switch')).toLowerCase();
|
||||
const service = nextState === 'on' ? 'turn_on' : 'turn_off';
|
||||
const source = String(options?.source || 'unknown');
|
||||
await callHomeAssistantService(domain, service, { entity_id: entityId });
|
||||
if (domain === 'light' && service === 'turn_on') {
|
||||
await turnOnLightAtFullBrightness(entityId);
|
||||
} else {
|
||||
// Off commands and non-light domains do not accept a meaningful light
|
||||
// brightness value, so their existing Home Assistant payload stays
|
||||
// intentionally unchanged.
|
||||
await callHomeAssistantService(domain, service, { entity_id: entityId });
|
||||
}
|
||||
logger.info('Issued Home Assistant command', { entityId, domain, service, source });
|
||||
}
|
||||
|
||||
@@ -467,7 +495,7 @@ function createRuntimeEngine(deps) {
|
||||
if (!runtime.connection) throw new Error('Home Assistant not connected');
|
||||
|
||||
const normalized = normalizeRgbColor(color);
|
||||
await callHomeAssistantService('light', 'turn_on', { entity_id: entityId, rgb_color: normalized });
|
||||
await turnOnLightAtFullBrightness(entityId, { rgb_color: normalized });
|
||||
logger.info('Issued Home Assistant color command', { entityId, rgbColor: normalized });
|
||||
}
|
||||
|
||||
@@ -481,7 +509,7 @@ function createRuntimeEngine(deps) {
|
||||
const normalizedKelvin = Number.isFinite(nextKelvin)
|
||||
? Math.max(2000, Math.min(6500, Math.round(nextKelvin)))
|
||||
: DEFAULT_WHITE_KELVIN;
|
||||
await callHomeAssistantService('light', 'turn_on', { entity_id: entityId, color_temp_kelvin: normalizedKelvin });
|
||||
await turnOnLightAtFullBrightness(entityId, { color_temp_kelvin: normalizedKelvin });
|
||||
logger.info('Issued Home Assistant white command', { entityId, colorTempKelvin: normalizedKelvin });
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Home Assistant Runtime Engine Tests
|
||||
// Purpose: Verifies the service-wide full-brightness rule for light commands.
|
||||
// Scope: Exercises injected Home Assistant calls without opening a real connection or starting the server.
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const test = require('node:test');
|
||||
const { createRuntimeEngine } = require('./runtimeEngine');
|
||||
const { entityConfig, entityState, runtime } = require('./state');
|
||||
|
||||
function createHarness() {
|
||||
const calls = [];
|
||||
const engine = createRuntimeEngine({
|
||||
enabled: true,
|
||||
haConfig: { whiteKelvin: 4000 },
|
||||
callHomeAssistantService: async (domain, service, serviceData) => {
|
||||
calls.push({ domain, service, serviceData });
|
||||
},
|
||||
// These tests only verify outbound service payloads. A no-op logger keeps
|
||||
// the harness faithful to the runtime dependency contract without adding
|
||||
// unrelated output to the test run.
|
||||
logger: {
|
||||
info() {},
|
||||
warn() {},
|
||||
},
|
||||
});
|
||||
|
||||
return { calls, engine };
|
||||
}
|
||||
|
||||
test('light interactions force full brightness without changing switches or off commands', async (t) => {
|
||||
const { calls, engine } = createHarness();
|
||||
|
||||
/*
|
||||
runtimeEngine uses the shared entity registry populated from configuration
|
||||
in production. Seed the smallest representative registry here and restore
|
||||
the shared state afterward so this focused unit test cannot leak state into
|
||||
other Home Assistant tests added later.
|
||||
*/
|
||||
entityConfig.clear();
|
||||
entityState.clear();
|
||||
entityConfig.set('light.room', { id: 'light.room', type: 'light', domain: 'light' });
|
||||
entityConfig.set('switch.lamp', { id: 'switch.lamp', type: 'switch', domain: 'switch' });
|
||||
runtime.connection = {};
|
||||
t.after(() => {
|
||||
entityConfig.clear();
|
||||
entityState.clear();
|
||||
runtime.connection = null;
|
||||
});
|
||||
|
||||
await engine.setEntityState('light.room', 'on');
|
||||
await engine.setLightColor('light.room', [12, 34, 56]);
|
||||
await engine.setLightWhite('light.room', 4500);
|
||||
await engine.setEntityState('light.room', 'off');
|
||||
await engine.setEntityState('switch.lamp', 'on');
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
{
|
||||
domain: 'light',
|
||||
service: 'turn_on',
|
||||
serviceData: { entity_id: 'light.room', brightness_pct: 100 },
|
||||
},
|
||||
{
|
||||
domain: 'light',
|
||||
service: 'turn_on',
|
||||
serviceData: { entity_id: 'light.room', rgb_color: [12, 34, 56], brightness_pct: 100 },
|
||||
},
|
||||
{
|
||||
domain: 'light',
|
||||
service: 'turn_on',
|
||||
serviceData: { entity_id: 'light.room', color_temp_kelvin: 4500, brightness_pct: 100 },
|
||||
},
|
||||
{
|
||||
domain: 'light',
|
||||
service: 'turn_off',
|
||||
serviceData: { entity_id: 'light.room' },
|
||||
},
|
||||
{
|
||||
domain: 'switch',
|
||||
service: 'turn_on',
|
||||
serviceData: { entity_id: 'switch.lamp' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
Reference in New Issue
Block a user