Files
smart-home-tablet/lib/tools/scenes.ts
d.klimov be0a731738
All checks were successful
Deploy / deploy (push) Successful in 1m52s
voice: усиление умного дома — контекст, промпт, новые tools
- промпт: перечислены ВСЕ 15+ tools, разрешено уверенно управлять домом
- route.ts: живой контекст (время + снимок устройств из HA) в системный промпт
  для LLM-пути (defensive, с таймаутом)
- новые tools: control_climate (кондей/термостат), control_tv, set_scene
  (night/movie/morning/away), all_off, ha_service (универсальный HA с whitelist)
- env: CLIMATE_ENTITY, TV_ENTITY (см. RUNBOOK)
2026-07-22 23:49:50 +03:00

101 lines
3.7 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'
/**
* Сцены (макросы над несколькими устройствами) и «выключить всё».
* Entity-конфиг переиспользует те же переменные, что control_light/tv/climate.
*/
function lightIds(): string[] {
const def = ['light.bedside', 'light.reading']
try {
const raw = process.env.LIGHT_ENTITIES_JSON
if (raw) {
const m = JSON.parse(raw) as Record<string, string>
const ids = Object.entries(m).filter(([k]) => k !== 'default').map(([, v]) => v)
return ids.length ? Array.from(new Set(ids)) : def
}
} catch {}
return def
}
const tvId = () => process.env.TV_ENTITY || 'media_player.tv'
const purifierId = 'fan.air_purifier'
async function ha(domain: string, service: string, entity_id: string, extra: Record<string, any> = {}) {
try {
await tabletJson('POST', '/api/voice/tools/smart-home', { domain, service, entity_id, ...extra })
} catch { /* устройство может быть mock — не валим сцену */ }
}
async function lightsOff() { await Promise.all(lightIds().map(id => ha('light', 'turn_off', id))) }
async function lightsOn(pct?: number) {
await Promise.all(lightIds().map(id => ha('light', 'turn_on', id, pct != null ? { brightness_pct: pct } : {})))
}
const setScene: VoiceTool = {
schema: {
type: 'function',
function: {
name: 'set_scene',
description:
'Активировать сцену — одной командой меняет несколько устройств. ' +
'night (ночь: свет выкл, очиститель в ночной режим), movie (кино: свет приглушить, ТВ вкл), ' +
'morning (утро: свет вкл, очиститель авто), away (ушёл: всё выключить). ' +
'Используй для «включи сцену ночь», «режим кино», «я ушёл».',
parameters: {
type: 'object',
properties: {
scene: { type: 'string', enum: ['night', 'movie', 'morning', 'away'], description: 'Какая сцена' },
},
required: ['scene'],
},
},
},
async execute(args) {
const scene = String(args?.scene || '')
switch (scene) {
case 'night':
await lightsOff()
await ha('fan', 'set_preset_mode', purifierId, { preset_mode: 'Night' })
break
case 'movie':
await lightsOn(20)
await ha('media_player', 'turn_on', tvId())
await ha('fan', 'set_preset_mode', purifierId, { preset_mode: 'Night' })
break
case 'morning':
await lightsOn()
await ha('fan', 'set_preset_mode', purifierId, { preset_mode: 'Auto' })
break
case 'away':
await lightsOff()
await ha('media_player', 'turn_off', tvId())
await ha('fan', 'turn_off', purifierId)
break
default:
return { error: `unknown scene: ${scene}` }
}
return { success: true, scene }
},
}
const allOff: VoiceTool = {
schema: {
type: 'function',
function: {
name: 'all_off',
description: 'Выключить всё: весь свет, телевизор, очиститель. Для «выключи всё», «отключи всё дома».',
parameters: { type: 'object', properties: {} },
},
},
async execute() {
await lightsOff()
await ha('media_player', 'turn_off', tvId())
await ha('fan', 'turn_off', purifierId)
return { success: true, turned_off: 'lights, tv, purifier' }
},
}
export const tools: VoiceTool[] = [setScene, allOff]