Skip to main content
Glama
rooms.ts16.1 kB
import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { HueClient } from '../hue-client.js'; export function createRoomTools(_client: HueClient): Tool[] { return [ { name: 'list_rooms', description: 'List all rooms in the Hue system', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_room', description: 'Get detailed information about a specific room including all lights', inputSchema: { type: 'object', properties: { roomId: { type: 'string', description: 'The ID of the room', }, }, required: ['roomId'], }, }, { name: 'control_room_lights', description: 'Control all lights in a room at once', inputSchema: { type: 'object', properties: { roomId: { type: 'string', description: 'The ID of the room', }, state: { type: 'object', description: 'The desired state for all lights in the room', properties: { on: { type: 'boolean', description: 'Whether the lights should be on or off', }, brightness: { type: 'number', description: 'Brightness level (0-100)', minimum: 0, maximum: 100, }, hue: { type: 'number', description: 'Hue in degrees (0-360)', minimum: 0, maximum: 360, }, saturation: { type: 'number', description: 'Saturation level (0-100)', minimum: 0, maximum: 100, }, colorTemp: { type: 'number', description: 'Color temperature in mireds (153-500)', minimum: 153, maximum: 500, }, transitionTime: { type: 'number', description: 'Transition time in milliseconds', minimum: 0, }, }, }, naturalLanguage: { type: 'string', description: 'Natural language description of desired state', }, useVariation: { type: 'boolean', description: 'Apply subtle variations between lights for more realistic atmospheric scenes', default: false, }, }, required: ['roomId'], oneOf: [ { required: ['state'] }, { required: ['naturalLanguage'] }, ], }, }, { name: 'list_zones', description: 'List all zones in the Hue system', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_zone', description: 'Get detailed information about a specific zone including all lights', inputSchema: { type: 'object', properties: { zoneId: { type: 'string', description: 'The ID of the zone', }, }, required: ['zoneId'], }, }, { name: 'control_zone_lights', description: 'Control all lights in a zone at once', inputSchema: { type: 'object', properties: { zoneId: { type: 'string', description: 'The ID of the zone', }, state: { type: 'object', description: 'The desired state for all lights in the zone', properties: { on: { type: 'boolean', description: 'Whether the lights should be on or off', }, brightness: { type: 'number', description: 'Brightness level (0-100)', minimum: 0, maximum: 100, }, hue: { type: 'number', description: 'Hue in degrees (0-360)', minimum: 0, maximum: 360, }, saturation: { type: 'number', description: 'Saturation level (0-100)', minimum: 0, maximum: 100, }, colorTemp: { type: 'number', description: 'Color temperature in mireds (153-500)', minimum: 153, maximum: 500, }, transitionTime: { type: 'number', description: 'Transition time in milliseconds', minimum: 0, }, }, }, naturalLanguage: { type: 'string', description: 'Natural language description of desired state', }, useVariation: { type: 'boolean', description: 'Apply subtle variations between lights for more realistic atmospheric scenes', default: false, }, }, required: ['zoneId'], oneOf: [ { required: ['state'] }, { required: ['naturalLanguage'] }, ], }, }, ]; } // Helper function to detect if a scene should use variation function shouldUseVariation(naturalLanguage?: string): boolean { if (!naturalLanguage) return false; const atmosphericKeywords = [ 'stormy', 'storm', 'thunderstorm', 'lightning', 'sunset', 'sunrise', 'dawn', 'dusk', 'twilight', 'fire', 'fireplace', 'campfire', 'candle', 'candlelight', 'ocean', 'forest', 'nature', 'atmospheric', 'moody', 'aurora', 'galaxy', 'cosmic', 'dreamy', 'evening', 'night', 'romantic', 'cozy' ]; const text = naturalLanguage.toLowerCase(); return atmosphericKeywords.some(keyword => text.includes(keyword)); } // Generate subtle variations for atmospheric lighting function generateLightVariations(baseState: any, numLights: number): any[] { const variations = []; for (let i = 0; i < numLights; i++) { const variation = { ...baseState }; // Apply subtle random variations if (baseState.hue !== undefined) { // Vary hue by ±5-15 degrees for color temperature variation const hueVariation = (Math.random() - 0.5) * 30; // ±15 degrees variation.hue = Math.max(0, Math.min(360, baseState.hue + hueVariation)); } if (baseState.saturation !== undefined) { // Vary saturation by ±5-10% for depth variation const satVariation = (Math.random() - 0.5) * 20; // ±10% variation.saturation = Math.max(0, Math.min(100, baseState.saturation + satVariation)); } if (baseState.brightness !== undefined) { // Vary brightness by ±10-20% for natural variation const brightnessVariation = (Math.random() - 0.5) * 40; // ±20% variation.brightness = Math.max(5, Math.min(100, baseState.brightness + brightnessVariation)); } if (baseState.colorTemp !== undefined) { // Vary color temperature by ±20-50 mireds for warmth variation const ctVariation = (Math.random() - 0.5) * 100; // ±50 mireds variation.colorTemp = Math.max(153, Math.min(500, baseState.colorTemp + ctVariation)); } // Add slight timing variations for more natural feel if (baseState.transitionTime !== undefined) { const timeVariation = Math.random() * 1000; // ±0.5s variation variation.transitionTime = baseState.transitionTime + timeVariation; } variations.push(variation); } return variations; } export async function handleListRooms(client: HueClient): Promise<any> { try { const rooms = await client.getRooms(); return { rooms: rooms.map(room => ({ id: room.id, name: room.name, type: room.type, class: room.class, lights: room.lights, lightCount: room.lights.length, state: { allOn: room.state.all_on, anyOn: room.state.any_on, }, })), count: rooms.length, }; } catch (error) { throw new Error(`Failed to list rooms: ${error}`); } } export async function handleGetRoom(client: HueClient, args: any): Promise<any> { try { const room = await client.getRoom(args.roomId); if (!room) { throw new Error(`Room ${args.roomId} not found`); } // Get lights in the room const lights = await client.getLightsInRoom(args.roomId); return { id: room.id, name: room.name, type: room.type, class: room.class, state: { allOn: room.state.all_on, anyOn: room.state.any_on, }, lights: lights.map(light => ({ id: light.id, name: light.name, type: light.type, state: { on: light.state.on, brightness: light.state.bri, reachable: light.state.reachable, colorMode: light.state.colormode, }, })), lightCount: lights.length, sensors: room.sensors, }; } catch (error) { throw new Error(`Failed to get room: ${error}`); } } export async function handleControlRoomLights(client: HueClient, args: any): Promise<any> { try { let baseState; if (args.naturalLanguage) { baseState = HueClient.parseNaturalLanguageState(args.naturalLanguage); } else { baseState = args.state; } // Check if variation is requested and if it's an atmospheric scene const useVariation = args.useVariation || shouldUseVariation(args.naturalLanguage); let success: boolean; let appliedStates: any[] = []; if (useVariation && (baseState.hue !== undefined || baseState.colorTemp !== undefined)) { // Apply individual variations for atmospheric scenes const room = await client.getRoom(args.roomId); if (!room) { throw new Error(`Room ${args.roomId} not found`); } const variations = generateLightVariations(baseState, room.lights.length); // Apply each variation to individual lights const results = await Promise.all( room.lights.map(async (lightId: string, index: number) => { const variation = variations[index]; const result = await client.setLightState(lightId, variation); appliedStates.push({ lightId, state: variation, success: result }); return result; }) ); success = results.every(result => result); } else { // Use standard room control for uniform lighting success = await client.setRoomState(args.roomId, baseState); appliedStates = [{ roomId: args.roomId, state: baseState, success }]; } if (!success) { throw new Error(`Failed to update room ${args.roomId}`); } // Get updated room state const room = await client.getRoom(args.roomId); const lights = await client.getLightsInRoom(args.roomId); return { success: true, roomId: args.roomId, roomName: room?.name, appliedState: useVariation ? 'Individual variations applied' : baseState, lightingMode: useVariation ? 'varied_atmospheric' : 'uniform', variationUsed: useVariation, appliedStates: useVariation ? appliedStates.map(state => ({ lightId: state.lightId, variation: state.state, })) : undefined, currentState: room ? { allOn: room.state.all_on, anyOn: room.state.any_on, } : null, affectedLights: lights.length, efficiency: useVariation ? `Applied ${lights.length} individual variations for atmospheric realism` : `Applied uniform state to ${lights.length} lights efficiently`, }; } catch (error) { throw new Error(`Failed to control room lights: ${error}`); } } export async function handleListZones(client: HueClient): Promise<any> { try { const zones = await client.getZones(); return { zones: zones.map(zone => ({ id: zone.id, name: zone.name, type: zone.type, lights: zone.lights, lightCount: zone.lights.length, state: { allOn: zone.state.all_on, anyOn: zone.state.any_on, }, })), count: zones.length, }; } catch (error) { throw new Error(`Failed to list zones: ${error}`); } } export async function handleGetZone(client: HueClient, args: any): Promise<any> { try { const zone = await client.getZone(args.zoneId); if (!zone) { throw new Error(`Zone ${args.zoneId} not found`); } // Get lights in the zone const lights = await client.getLightsInZone(args.zoneId); return { id: zone.id, name: zone.name, type: zone.type, class: zone.class, state: { allOn: zone.state.all_on, anyOn: zone.state.any_on, }, lights: lights.map(light => ({ id: light.id, name: light.name, type: light.type, state: { on: light.state.on, brightness: light.state.bri, reachable: light.state.reachable, colorMode: light.state.colormode, }, })), lightCount: lights.length, }; } catch (error) { throw new Error(`Failed to get zone: ${error}`); } } export async function handleControlZoneLights(client: HueClient, args: any): Promise<any> { try { let baseState; if (args.naturalLanguage) { baseState = HueClient.parseNaturalLanguageState(args.naturalLanguage); } else { baseState = args.state; } // Check if variation is requested and if it's an atmospheric scene const useVariation = args.useVariation || shouldUseVariation(args.naturalLanguage); let success: boolean; let appliedStates: any[] = []; if (useVariation && (baseState.hue !== undefined || baseState.colorTemp !== undefined)) { // Apply individual variations for atmospheric scenes const zone = await client.getZone(args.zoneId); if (!zone) { throw new Error(`Zone ${args.zoneId} not found`); } const variations = generateLightVariations(baseState, zone.lights.length); // Apply each variation to individual lights const results = await Promise.all( zone.lights.map(async (lightId: string, index: number) => { const variation = variations[index]; const result = await client.setLightState(lightId, variation); appliedStates.push({ lightId, state: variation, success: result }); return result; }) ); success = results.every(result => result); } else { // Use standard zone control for uniform lighting success = await client.setZoneState(args.zoneId, baseState); appliedStates = [{ zoneId: args.zoneId, state: baseState, success }]; } if (!success) { throw new Error(`Failed to update zone ${args.zoneId}`); } // Get updated zone state const zone = await client.getZone(args.zoneId); const lights = await client.getLightsInZone(args.zoneId); return { success: true, zoneId: args.zoneId, zoneName: zone?.name, appliedState: useVariation ? 'Individual variations applied' : baseState, lightingMode: useVariation ? 'varied_atmospheric' : 'uniform', variationUsed: useVariation, appliedStates: useVariation ? appliedStates.map(state => ({ lightId: state.lightId, variation: state.state, })) : undefined, currentState: zone ? { allOn: zone.state.all_on, anyOn: zone.state.any_on, } : null, affectedLights: lights.length, efficiency: useVariation ? `Applied ${lights.length} individual variations for atmospheric realism` : `Applied uniform state to ${lights.length} lights efficiently`, }; } catch (error) { throw new Error(`Failed to control zone lights: ${error}`); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rmrfslashbin/hue-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server