mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-11-14 18:58:49 -05:00
home assistant entities yay yay
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
// The shared dispatcher supplies admin/lockdown authorization for both chat
|
||||
// transports. Preserve the remaining tokens so names with spaces need no quotes.
|
||||
const { getCommandConfig } = require('../config');
|
||||
|
||||
function createHaCommand({ homeAssistantActivitiesService: service, config }) {
|
||||
return async function handleHaCommand(message, tokens = []) {
|
||||
const reply = (content) => message.reply({ content, allowedMentions: { parse: [], repliedUser: false } });
|
||||
if (!service) return reply('Home Assistant activities are unavailable.');
|
||||
const action = String(tokens[0] || 'status').toLowerCase();
|
||||
try {
|
||||
if (action === 'status') {
|
||||
const items = service.getState().items;
|
||||
return reply(items.length ? items.map((item) => `${item.name} (${item.id}): ${item.locked ? 'locked' : 'unlocked'}`).join('\n') : 'No activity items configured.');
|
||||
}
|
||||
if (!['lock', 'unlock'].includes(action) || tokens.length < 2) {
|
||||
return reply(`Use ${getCommandConfig(config).prefix} ha status, or ha <lock|unlock> <name or entity ID>.`);
|
||||
}
|
||||
const item = service.setLocked(tokens.slice(1).join(' '), action === 'lock');
|
||||
return reply(`${item.name} (${item.id}) ${action === 'lock' ? 'locked' : 'unlocked'}.`);
|
||||
} catch (error) {
|
||||
return reply(error.message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { createHaCommand };
|
||||
@@ -9,6 +9,7 @@ const { createGoalCommand } = require('./commands/goal');
|
||||
const { createVerifyCommand } = require('./commands/verify');
|
||||
const { createDeterCommand } = require('./commands/deter');
|
||||
const { createPermissionsCommand } = require('./commands/permissions');
|
||||
const { createHaCommand } = require('./commands/ha');
|
||||
const { createLightsCommand } = require('./commands/lights');
|
||||
const { createGreenCommand } = require('./commands/green');
|
||||
const { createKickCommand } = require('./commands/kick');
|
||||
@@ -61,6 +62,8 @@ function createCommandHandlers(deps) {
|
||||
const handlePermissionsCommand = createPermissionsCommand(deps);
|
||||
const handleBridgeCommand = transportHandlers.bridge;
|
||||
const handleTimeStatusCommand = transportHandlers.timeStatus;
|
||||
// HA activities have their own namespace; lights remains room-only.
|
||||
const handleHaCommand = createHaCommand(deps);
|
||||
const handleLightsCommand = createLightsCommand(deps);
|
||||
const handleGreenCommand = createGreenCommand(deps);
|
||||
const handleKickCommand = createKickCommand(deps);
|
||||
@@ -110,7 +113,7 @@ function createCommandHandlers(deps) {
|
||||
// is included because its lock/unlock subcommands change room policy. Its
|
||||
// ordinary on/off/color actions are also intentionally restricted to a
|
||||
// lockdown admin while the entire server is in lockdown.
|
||||
const moderationActions = new Set(['lock', 'unlock', 'mode', 'goal', 'reason', 'verify', 'deter', 'permissions', 'lights', 'green', 'kick', 'lift', 'neato']);
|
||||
const moderationActions = new Set(['lock', 'unlock', 'mode', 'goal', 'reason', 'verify', 'deter', 'permissions', 'lights', 'ha', 'green', 'kick', 'lift', 'neato']);
|
||||
const isAccessModeCommand = commandDefinition?.permission === 'access-mode';
|
||||
|
||||
// Feature commands are public activities while access is open or managed
|
||||
@@ -144,6 +147,8 @@ function createCommandHandlers(deps) {
|
||||
case 'bridge':
|
||||
if (!handleBridgeCommand) return request.reply(formatHelp({ commandPrefix, timeStatusCommand, includeDiscord: false, isFeatureEnabled: deps.isFeatureEnabled }));
|
||||
return handleBridgeCommand(request, tokens);
|
||||
case 'ha':
|
||||
return handleHaCommand(request, tokens);
|
||||
case 'lights':
|
||||
return handleLightsCommand(request, tokens);
|
||||
case 'green':
|
||||
|
||||
@@ -75,11 +75,17 @@ const admin = { id: 's1', userId: 'u-alice', label: 'alice', isAdmin: true, isLo
|
||||
|
||||
test('admin-only commands stay admin-only for a non-admin', async () => {
|
||||
const run = createRouter();
|
||||
for (const command of ['rs lock rover-1', 'rs unlock rover-1', 'rs mode open', 'rs green on', 'rs kick alice', 'rs permissions list']) {
|
||||
for (const command of ['rs lock rover-1', 'rs unlock rover-1', 'rs mode open', 'rs green on', 'rs kick alice', 'rs permissions list', 'rs ha lock Fan speed']) {
|
||||
assert.match(await run(command, nonAdmin), ADMIN_DENIAL, `${command} must stay admin-only`);
|
||||
}
|
||||
});
|
||||
|
||||
// Activity locks obey the same stronger restriction as other moderation actions.
|
||||
test('activity lock commands require lockdown admin during lockdown', async () => {
|
||||
const run = createRouter({ mode: MODES.LOCKDOWN });
|
||||
assert.match(await run('rs ha unlock Fan speed', admin), LOCKDOWN_DENIAL);
|
||||
});
|
||||
|
||||
test('commands that police themselves still reach their handler as a non-admin', async () => {
|
||||
const run = createRouter();
|
||||
// These reply with their own role-specific message, so the dispatcher must not
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
const CATEGORIES = {
|
||||
system: { title: 'System', names: ['help', 'status', 'replay', 'time-status'] },
|
||||
admin: { title: 'Admin', names: ['lock', 'unlock', 'mode', 'reason', 'goal', 'green', 'kick', 'verify', 'deter', 'permissions'] },
|
||||
features: { title: 'Features', names: ['lights', 'lift', 'neato'] },
|
||||
features: { title: 'Features', names: ['lights', 'ha', 'lift', 'neato'] },
|
||||
discord: { title: 'Discord', names: ['bridge'] },
|
||||
};
|
||||
|
||||
@@ -23,6 +23,8 @@ function buildCommandRegistry(prefix, timeCommand) {
|
||||
// an optional enhancement, so the command must remain available when that
|
||||
// integration is absent.
|
||||
green: { category: 'admin', summary: 'Toggle green room and page mode.', usage: [`${prefix} green <on|off>`], access: 'Admin', permission: 'admin' },
|
||||
// Locks are moderation only; entity actions remain in the Activities card.
|
||||
ha: { category: 'features', summary: 'List activity locks or lock/unlock an item by name or entity ID.', usage: [`${prefix} ha status`, `${prefix} ha <lock|unlock> <name or entity ID>`], access: 'Admin', permission: 'admin', requiredFeature: 'homeAssistantActivities', unavailableLabel: 'Home Assistant activities' },
|
||||
lights: {
|
||||
category: 'features',
|
||||
summary: 'Control room lights or manage the admin light lock.',
|
||||
|
||||
Reference in New Issue
Block a user