mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
127 lines
4.5 KiB
JavaScript
127 lines
4.5 KiB
JavaScript
// Home Assistant Hooks
|
|
// Purpose: Registers turn/mode/socket event handlers that invoke Home Assistant runtime operations.
|
|
// Scope: Owns permission-gated socket routes and service-triggered automation reevaluation wiring.
|
|
const io = require('../../globals/io');
|
|
const { getMode, MODES, modeEvents } = require('../modeManager');
|
|
const { isAdmin, isLockdownAdmin } = require('../roleService');
|
|
|
|
function registerHomeAssistantHooks(deps) {
|
|
const {
|
|
logger,
|
|
getHaConfig,
|
|
isLightControlLocked,
|
|
setLightsLockedOn,
|
|
toggleEntity,
|
|
setEntityState,
|
|
setLightColor,
|
|
setLightWhite,
|
|
} = deps;
|
|
|
|
modeEvents.on('change', (mode) => {
|
|
if (mode === MODES.ADMIN || mode === MODES.LOCKDOWN) {
|
|
if (isLightControlLocked()) {
|
|
setLightsLockedOn(false, { source: 'modeGateReset' }).catch((err) => {
|
|
logger.warn('Failed to disable lights lock on mode change', err.message);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
io.on('connection', (socket) => {
|
|
function hasPermission() {
|
|
const mode = getMode();
|
|
if (mode === 'admin' && isAdmin(socket) !== true) return false;
|
|
if (mode === 'lockdown' && isLockdownAdmin(socket) !== true) return false;
|
|
return true;
|
|
}
|
|
|
|
function isBlockedByRoomControlLock() {
|
|
/*
|
|
The lock is meant to keep normal users and automated room-control
|
|
surfaces from changing the preferred room-light policy. Admins are the
|
|
exception because they may need to correct a single lamp, verify a Home
|
|
Assistant integration, or make an operational adjustment while the
|
|
public controls remain locked.
|
|
|
|
This server-side bypass is the authoritative rule. The React UI also
|
|
enables admin controls for usability, but clients are not trusted to
|
|
enforce permissions.
|
|
*/
|
|
return isLightControlLocked() && !isAdmin(socket);
|
|
}
|
|
|
|
socket.on('homeAssistant:toggle', async ({ entityId } = {}, cb = () => {}) => {
|
|
if (!hasPermission()) {
|
|
return cb({ error: 'Insufficient permissions to control Home Assistant' });
|
|
}
|
|
if (isBlockedByRoomControlLock()) {
|
|
return cb({ error: 'Room controls are locked' });
|
|
}
|
|
try {
|
|
if (!entityId) throw new Error('entityId required');
|
|
await toggleEntity(entityId, { source: `socket:${socket.id}:homeAssistant:toggle` });
|
|
cb({ success: true });
|
|
} catch (err) {
|
|
cb({ error: err.message });
|
|
}
|
|
});
|
|
|
|
socket.on('homeAssistant:setState', async ({ entityId, state } = {}, cb = () => {}) => {
|
|
if (!hasPermission()) {
|
|
return cb({ error: 'Insufficient permissions to control Home Assistant' });
|
|
}
|
|
if (isBlockedByRoomControlLock()) {
|
|
return cb({ error: 'Room controls are locked' });
|
|
}
|
|
try {
|
|
if (!entityId) throw new Error('entityId required');
|
|
await setEntityState(entityId, state, { source: `socket:${socket.id}:homeAssistant:setState` });
|
|
cb({ success: true });
|
|
} catch (err) {
|
|
cb({ error: err.message });
|
|
}
|
|
});
|
|
|
|
socket.on('homeAssistant:lightColor', async ({ entityId, colorHex, rgbColor } = {}, cb = () => {}) => {
|
|
if (!hasPermission()) {
|
|
return cb({ error: 'Insufficient permissions to control Home Assistant' });
|
|
}
|
|
if (isBlockedByRoomControlLock()) {
|
|
return cb({ error: 'Room controls are locked' });
|
|
}
|
|
try {
|
|
if (!entityId) throw new Error('entityId required');
|
|
// New browser clients send colorHex because it is directly usable by
|
|
// React/CSS. rgbColor remains accepted for compatibility with any older
|
|
// client that still sends Home Assistant-style RGB arrays.
|
|
await setLightColor(entityId, colorHex ?? rgbColor);
|
|
cb({ success: true });
|
|
} catch (err) {
|
|
cb({ error: err.message });
|
|
}
|
|
});
|
|
|
|
socket.on('homeAssistant:lightWhite', async ({ entityId } = {}, cb = () => {}) => {
|
|
if (!hasPermission()) {
|
|
return cb({ error: 'Insufficient permissions to control Home Assistant' });
|
|
}
|
|
if (isBlockedByRoomControlLock()) {
|
|
return cb({ error: 'Room controls are locked' });
|
|
}
|
|
try {
|
|
if (!entityId) throw new Error('entityId required');
|
|
// Resolve configuration at interaction time because the socket handler
|
|
// is intentionally registered once and survives service reloads.
|
|
await setLightWhite(entityId, getHaConfig()?.whiteKelvin);
|
|
cb({ success: true });
|
|
} catch (err) {
|
|
cb({ error: err.message });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
registerHomeAssistantHooks,
|
|
};
|