[HOW-TO][Pro] Generate notification text with an OpenAI-compatible LLM endpoint (hosted or local) from HomeyScript

I work on the APIGOTO team. APIGOTO is an independent OpenAI-compatible gateway, not affiliated with OpenAI, Anthropic or Google. This builds on the “[HOW TO] use a local LLM to generate unique notifications” tutorial. The same script works against a local server or any hosted OpenAI-compatible endpoint, because only the base URL and model ID change.

What you need

  • Homey Pro (HomeyScript runs locally only).
  • The HomeyScript app, scripts at My Homey
  • An API key, and a model ID taken from GET <base URL>/models

1. Test the endpoint outside Homey

curl https://api.china-aiapi.com/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID","messages":[{"role":"user","content":"Say hi"}]}'

2. Store the key once

Create a script called set-llm-key, run it once, then delete the key from the code:

global.set('llm_api_key', 'PASTE_KEY_HERE');
log('saved');

3. The script that calls the model

Create a script called llm-notify:

const BASE_URL = 'https://api.china-aiapi.com/v1';   // or http://<lan-ip>:<port>/v1 for a local server
const MODEL = 'MODEL_ID';

const key = global.get('llm_api_key');
const facts = args[0] || 'The front door was opened.';

const res = await fetch(`${BASE_URL}/chat/completions`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
  body: JSON.stringify({
    model: MODEL,
    max_tokens: 80,
    messages: [
      { role: 'system', content: 'Write one short, friendly smart-home notification. No emojis, max 20 words.' },
      { role: 'user', content: facts },
    ],
  }),
  timeout: 20000, // node-fetch v2 option, in ms
});

if (!res.ok) {
  log('HTTP', res.status, (await res.text()).slice(0, 300));
  return facts; // fall back to the plain text so the Flow still notifies
}

const data = await res.json();
return (data.choices?.[0]?.message?.content || facts).trim();

4. The Flow

  • When: whatever event you want (door opened, washer finished…).
  • Then: HomeyScript → Run [code] with [argument] and return Text-tag. Pick llm-notify. As the argument, write the facts with tags, for example Washer finished. Outside temperature: [tag] °C.
  • Then: Send a push notification (or a speaker “say” card) with the Result tag from the card above.

The script returns the original facts on any error, so a dead endpoint never means a missing notification.

Troubleshooting

  • Nothing in the Result tag: open the script editor and run it with a test argument. log() output shows the HTTP status.
  • HTTP 401: the key wasn’t saved (run set-llm-key again) or has a trailing space.
  • HTTP 404: /v1 missing or doubled in BASE_URL.
  • “model not found”: MODEL doesn’t match /models exactly.
  • The script stops early: HomeyScript has a run timeout, so keep timeout short and max_tokens low. Notifications don’t need long answers.
  • Privacy: whatever you put in the argument (names, presence, temperatures) goes to the endpoint. A local server keeps it on your LAN.

On the gateway side: one base URL for several model families, automatic failover on upstream errors (at most 3 retries per request on another route), a per-request log, pay-as-you-go, BYOK: www.china-aiapi.com

Which events do you actually want “unique” wording for, and which do you keep fixed?