mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
overseer voting stuff yay!
This commit is contained in:
@@ -5,6 +5,7 @@ const logger = require('../../globals/logger').child('overseerControl');
|
||||
const { loadConfig } = require('../../helpers/configLoader');
|
||||
const { getRole, roleEvents } = require('../roleService');
|
||||
const { getMode, MODES, modeEvents } = require('../modeManager');
|
||||
const { verificationEvents } = require('../verificationService');
|
||||
const homeAssistantService = require('../homeAssistantService');
|
||||
const neatoService = require('../neatoService');
|
||||
const liftService = require('../liftService');
|
||||
@@ -89,6 +90,7 @@ let status = {
|
||||
lastLiveToolCalls: null,
|
||||
lastOutcome: null,
|
||||
lastReason: null,
|
||||
voteStatus: null,
|
||||
lastError: null,
|
||||
lastErrorDetails: null,
|
||||
lastFailedAt: null,
|
||||
@@ -98,6 +100,26 @@ let status = {
|
||||
updatedAt: Date.now(),
|
||||
};
|
||||
|
||||
function buildVoteStatus() {
|
||||
const sockets = Array.from(io.sockets.sockets.values());
|
||||
let yesCount = 0;
|
||||
let noCount = 0;
|
||||
sockets.forEach((socket) => {
|
||||
const pref = socket?.data?.overseerEnabled;
|
||||
if (typeof pref === 'boolean' ? pref : true) yesCount += 1;
|
||||
else noCount += 1;
|
||||
});
|
||||
const onlineCount = yesCount + noCount;
|
||||
const gatePassed = yesCount > noCount;
|
||||
return {
|
||||
yesCount,
|
||||
noCount,
|
||||
onlineCount,
|
||||
gatePassed,
|
||||
running: Boolean(enabled && gatePassed && getMode() !== MODES.LOCKDOWN),
|
||||
};
|
||||
}
|
||||
|
||||
function updateStatus(patch = {}) {
|
||||
status = { ...status, ...patch, updatedAt: Date.now() };
|
||||
const payload = buildAdminState(status, runtime.runHistory);
|
||||
@@ -460,8 +482,29 @@ function startScheduler(reason = null) {
|
||||
runtime.timer = setTimeout(tick, gateIntervalMs);
|
||||
}
|
||||
|
||||
function evaluateSchedulerGate(reason = 'gate reevaluated') {
|
||||
const voteStatus = buildVoteStatus();
|
||||
const runningAllowed = Boolean(enabled && voteStatus.gatePassed && getMode() !== MODES.LOCKDOWN);
|
||||
updateStatus({ voteStatus });
|
||||
if (!enabled) {
|
||||
stopScheduler('overseerControl.enabled is false');
|
||||
return;
|
||||
}
|
||||
if (getMode() === MODES.LOCKDOWN) {
|
||||
stopScheduler('paused during lockdown');
|
||||
return;
|
||||
}
|
||||
if (!voteStatus.gatePassed) {
|
||||
stopScheduler('paused by user vote');
|
||||
return;
|
||||
}
|
||||
startScheduler(reason);
|
||||
}
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
emitStateToSocket(socket);
|
||||
evaluateSchedulerGate('online vote update');
|
||||
socket.on('disconnect', () => evaluateSchedulerGate('online vote update'));
|
||||
socket.on('overseer:control', ({ controls } = {}, cb = () => {}) => {
|
||||
if (!isAdminRole(getRole(socket))) return cb({ error: 'Not authorized' });
|
||||
const action = controls?.action || null;
|
||||
@@ -474,6 +517,7 @@ io.on('connection', (socket) => {
|
||||
});
|
||||
|
||||
roleEvents.on('change', ({ socket }) => emitStateToSocket(socket));
|
||||
verificationEvents.on('change', () => evaluateSchedulerGate('online vote update'));
|
||||
homeAssistantEvents.on('update', () => updateStatus({ phase: status.phase }));
|
||||
neatoEvents.on('update', () => updateStatus({ phase: status.phase }));
|
||||
liftEvents.on('update', () => updateStatus({ phase: status.phase }));
|
||||
@@ -485,7 +529,7 @@ modeEvents.on('change', (mode) => {
|
||||
logger.info('overseerControl paused due to lockdown mode');
|
||||
return;
|
||||
}
|
||||
startScheduler(observeOnly ? 'observe-only mode' : null);
|
||||
evaluateSchedulerGate(observeOnly ? 'observe-only mode' : null);
|
||||
});
|
||||
|
||||
if (!enabled) {
|
||||
@@ -496,9 +540,11 @@ if (!enabled) {
|
||||
stopScheduler('paused during lockdown');
|
||||
logger.info('overseerControl paused on startup due to lockdown mode');
|
||||
} else {
|
||||
startScheduler(observeOnly ? 'observe-only mode' : null);
|
||||
evaluateSchedulerGate(observeOnly ? 'observe-only mode' : null);
|
||||
logger.info('overseerControl enabled', { model, ollamaUrl, gateIntervalMs, heartbeatMs, observeOnly });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {};
|
||||
module.exports = {
|
||||
getVoteStatus: () => status.voteStatus || buildVoteStatus(),
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ const { getRoomCameras, roomCameraEvents } = require('../roomCameraService');
|
||||
const { getState: getHomeAssistantState, homeAssistantEvents } = require('../homeAssistantService');
|
||||
const { getState: getNeatoState, neatoEvents } = require('../neatoService');
|
||||
const { getState: getLiftState, liftEvents } = require('../liftService');
|
||||
const { getVoteStatus: getOverseerVoteStatus } = require('../overseerControlService');
|
||||
const { getNickname, nicknameEvents } = require('../nicknameService');
|
||||
const {
|
||||
getVerificationStateForSocket,
|
||||
@@ -66,6 +67,7 @@ function buildUserEntry(socket) {
|
||||
}
|
||||
|
||||
function buildSession(socket) {
|
||||
const overseerVote = getOverseerVoteStatus();
|
||||
const users = Array.from(io.sockets.sockets.values())
|
||||
.map((sock) => buildUserEntry(sock))
|
||||
.filter(Boolean)
|
||||
@@ -124,6 +126,10 @@ function buildSession(socket) {
|
||||
audioForward: getAudioForwardState(),
|
||||
audioLevels: getAudioLevels(),
|
||||
buttonBox: getButtonBoxState(),
|
||||
overseerVote: {
|
||||
...overseerVote,
|
||||
preference: typeof socket?.data?.overseerEnabled === 'boolean' ? socket.data.overseerEnabled : true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -111,11 +111,18 @@ function identifySocket(socket, payload = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(payload, 'overseerEnabled')) {
|
||||
data.overseerEnabled = Boolean(payload.overseerEnabled);
|
||||
} else if (typeof data.overseerEnabled !== 'boolean') {
|
||||
data.overseerEnabled = true;
|
||||
}
|
||||
|
||||
const verification = reevaluateSocketVerification(socket);
|
||||
const deterrence = reevaluateSocketDeterrence(socket);
|
||||
emitChange('identify', { socketId: socket.id });
|
||||
return {
|
||||
cookieUserId: data.cookieUserId,
|
||||
overseerEnabled: Boolean(data.overseerEnabled),
|
||||
isVerified: verification.isVerified,
|
||||
isDeterred: deterrence.isDeterred,
|
||||
reason: verification.reason,
|
||||
|
||||
@@ -97,6 +97,7 @@ function createVerificationFlow(deps) {
|
||||
return {
|
||||
cookieUserId: normalizeCookieUserId(data.cookieUserId) || null,
|
||||
nickname: getNickname(socket) || null,
|
||||
overseerEnabled: typeof data.overseerEnabled === 'boolean' ? data.overseerEnabled : true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user