This commit is contained in:
legop3
2026-04-29 19:38:33 -04:00
parent c31fffec20
commit 97196ef202
5 changed files with 30 additions and 12 deletions
+2 -1
View File
@@ -80,7 +80,8 @@ function createButtonBoxCore(deps) {
return Array.isArray(entities) ? entities : []; return Array.isArray(entities) ? entities : [];
}, },
getHomeAssistantLightPolicy: () => getHomeAssistantState()?.lightPolicy || null, getHomeAssistantLightPolicy: () => getHomeAssistantState()?.lightPolicy || null,
setHomeAssistantEntityState, setHomeAssistantEntityState: (entityId, state) =>
setHomeAssistantEntityState(entityId, state, { source: 'buttonBoxReward' }),
setHomeAssistantLightsLockedOn: (next, options = {}) => setHomeAssistantLightsLockedOn: (next, options = {}) =>
setHomeAssistantLightsLockedOn(next, options), setHomeAssistantLightsLockedOn(next, options),
saveEffect: (effectId, payload = {}) => saveEffect(effectId, payload, { broadcast: false }), saveEffect: (effectId, payload = {}) => saveEffect(effectId, payload, { broadcast: false }),
@@ -44,7 +44,7 @@ function registerHomeAssistantHooks(deps) {
} }
try { try {
if (!entityId) throw new Error('entityId required'); if (!entityId) throw new Error('entityId required');
await toggleEntity(entityId); await toggleEntity(entityId, { source: `socket:${socket.id}:homeAssistant:toggle` });
cb({ success: true }); cb({ success: true });
} catch (err) { } catch (err) {
cb({ error: err.message }); cb({ error: err.message });
@@ -60,7 +60,7 @@ function registerHomeAssistantHooks(deps) {
} }
try { try {
if (!entityId) throw new Error('entityId required'); if (!entityId) throw new Error('entityId required');
await setEntityState(entityId, state); await setEntityState(entityId, state, { source: `socket:${socket.id}:homeAssistant:setState` });
cb({ success: true }); cb({ success: true });
} catch (err) { } catch (err) {
cb({ error: err.message }); cb({ error: err.message });
@@ -64,7 +64,7 @@ function createRuntimeEngine(deps) {
return Array.from(entityConfig.values()).map((meta) => String(meta.id)); return Array.from(entityConfig.values()).map((meta) => String(meta.id));
} }
async function setEntityState(entityId, desiredState) { async function setEntityState(entityId, desiredState, options = {}) {
if (!enabled) throw new Error('Home Assistant not configured'); if (!enabled) throw new Error('Home Assistant not configured');
const meta = entityConfig.get(entityId); const meta = entityConfig.get(entityId);
if (!meta) throw new Error('Unknown Home Assistant entity'); if (!meta) throw new Error('Unknown Home Assistant entity');
@@ -73,14 +73,23 @@ function createRuntimeEngine(deps) {
const nextState = desiredState === 'on' ? 'on' : 'off'; const nextState = desiredState === 'on' ? 'on' : 'off';
const domain = meta.type === 'light' ? 'light' : 'switch'; const domain = meta.type === 'light' ? 'light' : 'switch';
const service = nextState === 'on' ? 'turn_on' : 'turn_off'; const service = nextState === 'on' ? 'turn_on' : 'turn_off';
const source = String(options?.source || 'unknown');
await callHomeAssistantService(domain, service, { entity_id: entityId }); await callHomeAssistantService(domain, service, { entity_id: entityId });
logger.info('Issued Home Assistant command', { entityId, domain, service }); logger.info('Issued Home Assistant command', { entityId, domain, service, source });
} }
async function setAllControllableEntitiesState(desiredState) { async function setAllControllableEntitiesState(desiredState, options = {}) {
const source = String(options?.source || 'unknown');
const ids = getControllableEntityIds(); const ids = getControllableEntityIds();
if (!ids.length) return; if (!ids.length) return;
const results = await Promise.allSettled(ids.map((id) => setEntityState(id, desiredState))); logger.info('Issuing Home Assistant bulk state update', {
desiredState: desiredState === 'on' ? 'on' : 'off',
source,
total: ids.length,
});
const results = await Promise.allSettled(
ids.map((id) => setEntityState(id, desiredState, { source: `${source}:bulk` })),
);
const failures = results.filter((result) => result.status === 'rejected'); const failures = results.filter((result) => result.status === 'rejected');
if (failures.length) { if (failures.length) {
logger.warn('Some Home Assistant entity state updates failed', { logger.warn('Some Home Assistant entity state updates failed', {
@@ -200,10 +209,11 @@ function createRuntimeEngine(deps) {
evaluateTriggers(snapshot); evaluateTriggers(snapshot);
} }
async function toggleEntity(entityId) { async function toggleEntity(entityId, options = {}) {
const current = entityState.get(entityId); const current = entityState.get(entityId);
const nextState = current?.state === 'on' ? 'off' : 'on'; const nextState = current?.state === 'on' ? 'off' : 'on';
return setEntityState(entityId, nextState); const source = String(options?.source || 'homeAssistant:toggleEntity');
return setEntityState(entityId, nextState, { source });
} }
async function setLightColor(entityId, rgbColor) { async function setLightColor(entityId, rgbColor) {
@@ -250,7 +260,9 @@ function createRuntimeEngine(deps) {
if (runtime.lightsLockState != null) { if (runtime.lightsLockState != null) {
if ((changed || forceApply) && enabled) { if ((changed || forceApply) && enabled) {
await setAllControllableEntitiesState(runtime.lightsLockState); await setAllControllableEntitiesState(runtime.lightsLockState, {
source: String(options?.source || 'homeAssistant:setLightsLockedOn'),
});
} }
} else { } else {
evaluateLightAutomation(); evaluateLightAutomation();
+1 -1
View File
@@ -13,7 +13,7 @@ const {
} = require('./constants'); } = require('./constants');
async function turnOffRoomControls() { async function turnOffRoomControls() {
await homeAssistantService.setAllControllableEntitiesState('off'); await homeAssistantService.setAllControllableEntitiesState('off', { source: 'idleService:turnOffRoomControls' });
return { action: 'roomControlsOff' }; return { action: 'roomControlsOff' };
} }
+6 -1
View File
@@ -31,6 +31,7 @@ const state = {
lastActor: null, lastActor: null,
lastError: null, lastError: null,
}; };
let lastEmittedStateJson = null;
function sleep(ms) { function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms)); return new Promise((resolve) => setTimeout(resolve, ms));
@@ -92,7 +93,11 @@ function getState() {
} }
function emitUpdate() { function emitUpdate() {
events.emit('update', getState()); const next = getState();
const nextJson = JSON.stringify(next);
if (nextJson === lastEmittedStateJson) return;
lastEmittedStateJson = nextJson;
events.emit('update', next);
} }
function assertReady() { function assertReady() {