revise what counts for idle service

This commit is contained in:
legop3
2026-10-02 23:05:39 -04:00
parent bee1866a70
commit e8a407cd49
+16 -19
View File
@@ -4,12 +4,15 @@
const logger = require('../../globals/logger').child('idleService'); const logger = require('../../globals/logger').child('idleService');
const io = require('../../globals/io'); const io = require('../../globals/io');
const { getRole, roleEvents } = require('../roleService'); const { getRole, roleEvents } = require('../roleService');
const { getMode, MODES, modeEvents } = require('../modeManager');
const { isDeterred, verificationEvents } = require('../verificationService');
const { IDLE_TIMEOUT_MS } = require('./constants'); const { IDLE_TIMEOUT_MS } = require('./constants');
const { runtime } = require('./state'); const { runtime } = require('./state');
const { runIdleActions } = require('./actions'); const { runIdleActions } = require('./actions');
const activityControls = require('../homeAssistantActivitiesService'); const activityControls = require('../homeAssistantActivitiesService');
function getActivitySnapshot() { function getActivitySnapshot() {
const mode = getMode();
let onlineUsers = 0; let onlineUsers = 0;
let onlineAdmins = 0; let onlineAdmins = 0;
let onlineSpectators = 0; let onlineSpectators = 0;
@@ -29,12 +32,16 @@ function getActivitySnapshot() {
return; return;
} }
/*
Lockdown admins are counted with regular admins because both represent a if (
person with operator-level access who may be supervising the room without isDeterred(socket)
actively driving a rover. Plain users also count even before they request || (mode === MODES.ADMIN && role !== 'admin' && role !== 'lockdown')
control, which is the behavior this service now needs. || (mode === MODES.LOCKDOWN && role !== 'lockdown')
*/ ) {
onlineIgnored += 1;
return;
}
if (role === 'admin' || role === 'lockdown') { if (role === 'admin' || role === 'lockdown') {
onlineAdmins += 1; onlineAdmins += 1;
return; return;
@@ -90,7 +97,7 @@ function scheduleIdleTimer() {
runtime.deadlineAt = null; runtime.deadlineAt = null;
const activity = getActivitySnapshot(); const activity = getActivitySnapshot();
if (activity.totalActive > 0) { if (activity.totalActive > 0) {
logger.info('Idle automation skipped; user or admin online', activity); logger.info('Idle automation skipped; eligible operator online', activity);
return; return;
} }
runtime.lastTriggeredAt = Date.now(); runtime.lastTriggeredAt = Date.now();
@@ -114,18 +121,12 @@ function scheduleIdleTimer() {
function refreshIdleState() { function refreshIdleState() {
const activity = getActivitySnapshot(); const activity = getActivitySnapshot();
logger.info('Idle state refresh', activity); logger.info('Idle state refresh', activity);
// Share the exact idle presence policy; Activity Controls must not count spectators or invent a second role filter.
activityControls.setOperatorsOnline(activity.totalActive > 0); activityControls.setOperatorsOnline(activity.totalActive > 0);
if (activity.totalActive > 0) { if (activity.totalActive > 0) {
clearIdleTimer(); clearIdleTimer();
if (runtime.idleActionsCompleted) { if (runtime.idleActionsCompleted) {
logger.info('Idle action one-shot reset; operator is online again', activity); logger.info('Idle action one-shot reset; operator is online again', activity);
} }
/*
A user/admin coming online starts a new activity window. When the room
later becomes idle again, the cleanup pipeline should be allowed to run
once for that new idle period.
*/
runtime.idleActionsCompleted = false; runtime.idleActionsCompleted = false;
return; return;
} }
@@ -133,17 +134,13 @@ function refreshIdleState() {
} }
io.on('connection', (socket) => { io.on('connection', (socket) => {
/*
A user/admin can be online without ever touching rover controls, so socket
presence has to be a first-class idle signal. The disconnect hook is just as
important: it is what starts the idle timeout after the last non-spectator
leaves, even if no driving event happens around that departure.
*/
refreshIdleState(); refreshIdleState();
socket.on('disconnect', refreshIdleState); socket.on('disconnect', refreshIdleState);
}); });
roleEvents.on('change', refreshIdleState); roleEvents.on('change', refreshIdleState);
modeEvents.on('change', refreshIdleState);
verificationEvents.on('change', refreshIdleState);
refreshIdleState(); refreshIdleState();