Files
smart-home-tablet/lib/tools/lights.ts
d.klimov e6dab09c38
All checks were successful
Deploy / deploy (push) Successful in 3m43s
voice: локальный агент (fast-path), tool control_light, смелая тема, RUNBOOK
- lib/agent-local.ts + route.ts: команды-действия исполняет локальная intent-служба
  (под AGENT_URL, с fallback на LLM); инфо-запросы остаются у LLM
- lib/tools/lights.ts: голосовое управление светом через HA (control_light)
- app/globals.css + page.tsx: космическая indigo/aurora тема (токены, kiosk-safe)
- agent/: sidecar-служба (Dockerfile, predict.py), веса монтируются томом
- RUNBOOK.md: вывод в продакшен на мини-ПК
2026-07-22 21:48:24 +03:00

103 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { VoiceTool } from './_types'
import { tabletJson } from './_http'
/**
* Управление светом (лампами) через Home Assistant.
*
* Голосовой агент (tablet-agent) распознаёт интенты light_on/light_off и слот
* device («свет у кровати», «лампу для чтения»). Здесь имя лампы сопоставляется
* с entity_id Home Assistant.
*
* Реальные entity_id задаются переменной окружения LIGHT_ENTITIES_JSON (в
* tablet.env), например:
* LIGHT_ENTITIES_JSON={"bedside":"light.yeelight_bedside","reading":"light.reading_lamp","default":"light.bedside"}
* Ключи — короткие имена (см. DEVICE_ALIASES ниже). Пока переменная не задана,
* используются заглушки light.* — команды проходят как mock (HA вернёт success).
*/
// Короткое имя лампы → entity_id. Переопределяется LIGHT_ENTITIES_JSON.
const DEFAULT_ENTITIES: Record<string, string> = {
bedside: 'light.bedside', // «Свет у кровати»
reading: 'light.reading', // «Лампа для чтения»
default: 'light.bedside', // если лампа не названа — эта
}
function lightEntities(): Record<string, string> {
const raw = process.env.LIGHT_ENTITIES_JSON
if (raw) {
try {
return { ...DEFAULT_ENTITIES, ...JSON.parse(raw) }
} catch {
// некорректный JSON — падаем на дефолты
}
}
return DEFAULT_ENTITIES
}
// Разговорные имена ламп → короткий ключ. Слот device приходит куском текста.
const DEVICE_ALIASES: Array<[RegExp, string]> = [
[/крова|спальн|ночник|прикроват/i, 'bedside'],
[/чтени|настольн|рабоч/i, 'reading'],
]
function resolveEntity(device?: string): string {
const map = lightEntities()
if (device) {
for (const [re, key] of DEVICE_ALIASES) {
if (re.test(device) && map[key]) return map[key]
}
}
return map.default || 'light.bedside'
}
const controlLight: VoiceTool = {
schema: {
type: 'function',
function: {
name: 'control_light',
description:
'Управление светом (лампами): включить или выключить. ' +
'Лампы: «свет у кровати», «лампа для чтения». Если лампа не названа — ' +
'используется лампа по умолчанию. Используй для команд вроде «включи свет», ' +
'«выключи лампу для чтения», «зажги свет у кровати».',
parameters: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['turn_on', 'turn_off'],
description: 'Включить или выключить',
},
device: {
type: 'string',
description:
'Какая лампа: «свет у кровати», «лампа для чтения». ' +
'Опционально — без неё берётся лампа по умолчанию.',
},
brightness_pct: {
type: 'number',
description: 'Яркость 0-100 (опционально, только при turn_on)',
},
},
required: ['action'],
},
},
},
async execute(args) {
const action = String(args?.action || '')
if (action !== 'turn_on' && action !== 'turn_off') {
return { error: `unknown action: ${action}` }
}
const entity_id = resolveEntity(args?.device as string | undefined)
const payload: Record<string, any> = { domain: 'light', service: action, entity_id }
if (action === 'turn_on' && typeof args?.brightness_pct === 'number') {
payload.brightness_pct = Math.max(0, Math.min(100, Math.round(args.brightness_pct)))
}
await tabletJson('POST', '/api/voice/tools/smart-home', payload)
return { success: true, action, entity_id, ...(args?.device ? { device: args.device } : {}) }
},
}
export const tools: VoiceTool[] = [controlLight]