/** * alexa_speak.ts * 指定した Echo デバイスにテキストを読み上げさせる Windmill スクリプト * * パラメータ: * device - ドロップダウンから選択するデバイス(内部的にはシリアル番号) * text - 読み上げるテキスト */ const ALEXA_API_URL = "http://alexa_api:3500"; type DeviceOption = { value: string; label: string }; const FALLBACK_DEVICE_OPTIONS: DeviceOption[] = [ { value: "G0922H085165007R", label: "プレハブ (G0922H085165007R)" }, { value: "G8M2DB08522600RL", label: "リビングエコー1 (G8M2DB08522600RL)" }, { value: "G8M2DB08522503WF", label: "リビングエコー2 (G8M2DB08522503WF)" }, { value: "G0922H08525302K5", label: "オフィスの右エコー (G0922H08525302K5)" }, { value: "G0922H08525302J9", label: "オフィスの左エコー (G0922H08525302J9)" }, { value: "G8M2HN08534302XH", label: "寝室のエコー (G8M2HN08534302XH)" }, ]; // Windmill Dynamic Select: 引数名 `device` に対応する `DynSelect_device` と `device()` を定義 export type DynSelect_device = string; export async function device(): Promise { try { const res = await fetch(`${ALEXA_API_URL}/devices`); if (!res.ok) return FALLBACK_DEVICE_OPTIONS; const devices = (await res.json()) as Array<{ name?: string; serial?: string; family?: string; }>; const options = devices .filter((d) => d.family === "ECHO" && d.serial) .map((d) => ({ value: d.serial as string, label: `${d.name ?? d.serial} (${d.serial})`, })) .sort((a, b) => a.label.localeCompare(b.label, "ja")); return options.length > 0 ? options : FALLBACK_DEVICE_OPTIONS; } catch { return FALLBACK_DEVICE_OPTIONS; } } export async function main( device: DynSelect_device, text: string, ): Promise<{ ok: boolean; device: string; text: string }> { const res = await fetch(`${ALEXA_API_URL}/speak`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ device, text }), }); if (!res.ok) { const body = await res.json().catch(() => ({})); throw new Error( `alexa-api error ${res.status}: ${JSON.stringify(body)}` ); } return await res.json(); }