From 1359c701bb95c2cefc62064282b41576d5df1826 Mon Sep 17 00:00:00 2001 From: legop3 Date: Sat, 25 Apr 2026 20:21:38 -0400 Subject: [PATCH] smol changess --- server/prompts/commentary_system.txt | 2 +- server/src/services/discordBotService.js | 50 ++++++++++++++------- server/src/services/homeAssistantService.js | 29 ++++++++++++ 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/server/prompts/commentary_system.txt b/server/prompts/commentary_system.txt index 727dbb26..5e60ed6f 100644 --- a/server/prompts/commentary_system.txt +++ b/server/prompts/commentary_system.txt @@ -6,7 +6,7 @@ Always pay attention to the chat. Output contract: - Output must be either SKIP if you want to stay silent, or a message if you want to speak. -- Allow 10 skips before speaking again, unless someone is talking to you. +- Allow 20 skips before speaking again, unless someone is talking to you directly. - If you choose to speak, send only one line. - Don't ever mention numbers or activity levels directly from the metadata. They are for internal use only. - Don't repeat the same or similar message over and over. diff --git a/server/src/services/discordBotService.js b/server/src/services/discordBotService.js index 7d226790..de881996 100644 --- a/server/src/services/discordBotService.js +++ b/server/src/services/discordBotService.js @@ -92,6 +92,19 @@ let presenceInterval = null; let presenceShowGoal = false; const VERIFY_APPROVE_EMOJI = '✅'; const VERIFY_DENY_EMOJI = '❌'; +const ADMIN_ALERT_EVENT_TYPES = new Set([ + 'rover.online', + 'rover.offline', + 'rover.dockGuard', + 'battery.warn', + 'battery.urgent', + 'battery.docked', + 'battery.undocked', + 'battery.charging.start', + 'battery.charging.stop', + 'battery.locked', + 'battery.unlocked', +]); function sanitizeMentions(text) { if (!text) return ''; return String(text) @@ -169,7 +182,6 @@ function isCharging(sensors) { function buildRoverStatusSnapshot(record) { if (!record) return null; - if (!roverManager.canReplayRoverId(record.id)) return null; const sensors = record.lastSensor?.decoded || record.lastSensor?.sensors || null; const docked = Boolean(sensors?.chargingSources?.homeBase); const charging = isCharging(sensors); @@ -1208,10 +1220,11 @@ async function handleTimeStatusCommand(message) { function buildBatteryStatusEmbed(color, records = null) { const embed = buildEmbed({ title: 'Rover Battery Status', color: color || 0x2196f3 }); - const baseRecords = (records || Array.from(rovers.values())).filter((entry) => - roverManager.canReplayRoverId(entry?.id), - ); - const snapshots = baseRecords.map(buildRoverStatusSnapshot).filter(Boolean); + const sourceRecords = records || Array.from(rovers.values()); + const baseRecords = records + ? sourceRecords + : sourceRecords.filter((entry) => roverManager.canReplayRoverId(entry?.id)); + const snapshots = baseRecords.map((entry) => buildRoverStatusSnapshot(entry)).filter(Boolean); if (snapshots.length === 0) { embed.setDescription('No rovers online.'); return embed; @@ -1286,9 +1299,6 @@ function buildAccessModeEmbed(mode, color) { function buildBatteryCaption(type, payload) { const roverId = payload?.roverId || 'unknown'; - if (!roverManager.canReplayRoverId(roverId)) { - return null; - } const record = rovers.get(roverId) || findRoverRecord(roverId); const snapshot = buildRoverStatusSnapshot(record); const base = snapshot?.name || roverId; @@ -1323,6 +1333,12 @@ function buildBatteryCaption(type, payload) { } } +function getEventRoverRecord(payload) { + const roverId = String(payload?.roverId || '').trim(); + if (!roverId) return null; + return rovers.get(roverId) || findRoverRecord(roverId) || null; +} + async function announce({ channelId, content, @@ -1379,7 +1395,7 @@ function handleBusEvent(event) { const channels = discordConfig.channels || {}; const roles = discordConfig.roles || {}; const roverId = payload?.roverId || null; - if (roverId && !roverManager.canReplayRoverId(roverId)) { + if (roverId && !roverManager.canReplayRoverId(roverId) && !ADMIN_ALERT_EVENT_TYPES.has(type)) { return; } switch (type) { @@ -1474,7 +1490,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0xf0b651)], + embeds: [buildBatteryStatusEmbed(0xf0b651, [getEventRoverRecord(payload)].filter(Boolean))], }); break; case 'battery.urgent': @@ -1486,7 +1502,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0xe53935)], + embeds: [buildBatteryStatusEmbed(0xe53935, [getEventRoverRecord(payload)].filter(Boolean))], }); break; case 'battery.docked': @@ -1497,7 +1513,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0x2196f3)], + embeds: [buildBatteryStatusEmbed(0x2196f3, [getEventRoverRecord(payload)].filter(Boolean))], }); break; case 'battery.undocked': @@ -1508,7 +1524,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0x2196f3)], + embeds: [buildBatteryStatusEmbed(0x2196f3, [getEventRoverRecord(payload)].filter(Boolean))], }); break; case 'battery.charging.start': @@ -1519,7 +1535,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0x2196f3)], + embeds: [buildBatteryStatusEmbed(0x2196f3, [getEventRoverRecord(payload)].filter(Boolean))], }); break; case 'battery.charging.stop': @@ -1530,7 +1546,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0xf0b651)], + embeds: [buildBatteryStatusEmbed(0xf0b651, [getEventRoverRecord(payload)].filter(Boolean))], }); break; case 'battery.locked': @@ -1541,7 +1557,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0xf0b651)], + embeds: [buildBatteryStatusEmbed(0xf0b651, [getEventRoverRecord(payload)].filter(Boolean))], }); schedulePresenceRotation(); break; @@ -1553,7 +1569,7 @@ function handleBusEvent(event) { content: buildBatteryCaption(type, payload), title: 'Battery Status', description: null, - embeds: [buildBatteryStatusEmbed(0x4caf50)], + embeds: [buildBatteryStatusEmbed(0x4caf50, [getEventRoverRecord(payload)].filter(Boolean))], }); schedulePresenceRotation(); break; diff --git a/server/src/services/homeAssistantService.js b/server/src/services/homeAssistantService.js index df7ae2dc..d8d0aba8 100644 --- a/server/src/services/homeAssistantService.js +++ b/server/src/services/homeAssistantService.js @@ -6,6 +6,8 @@ const logger = require('../globals/logger').child('homeAssistantService'); const { loadConfig } = require('../helpers/configLoader'); const { getMode, MODES, modeEvents } = require('./modeManager'); const { isAdmin, isLockdownAdmin } = require('./roleService'); +const roverManager = require('./roverManager'); +const { issueCommand } = require('./commandService'); const { publishEvent } = require('./eventBus'); const { getActiveDrivers, turnEvents } = require('./turnService'); @@ -192,6 +194,30 @@ function getControllableEntityIds() { return Array.from(entityConfig.values()).map((meta) => String(meta.id)); } +function turnOffAllRoverNightVision() { + const records = Array.from(roverManager.rovers.values()); + let attempted = 0; + let failed = 0; + records.forEach((record) => { + if (!record?.ws) return; + if (!record?.meta?.nightVision?.enabled) return; + attempted += 1; + try { + issueCommand(record.id, { + type: 'nightVision', + nightVision: { action: 'off' }, + }); + } catch (err) { + failed += 1; + logger.warn('Failed to auto turn off rover night vision after idle', { + roverId: record.id, + error: err.message, + }); + } + }); + return { attempted, failed }; +} + function getActiveDriverCount() { const active = getActiveDrivers(); if (!active || typeof active !== 'object') return 0; @@ -225,8 +251,11 @@ function scheduleLightsIdleOffTimer() { lightsIdleOffDeadline = null; try { await setAllControllableEntitiesState('off'); + const nightVisionResult = turnOffAllRoverNightVision(); logger.info('Auto-turned off room lights due to no active drivers', { idleMs: LIGHT_IDLE_OFF_MS, + nightVisionRovers: nightVisionResult.attempted, + nightVisionFailures: nightVisionResult.failed, }); } catch (err) { logger.warn('Failed auto light-off after idle', err.message);