Files
kvida-os/components/lua_runtime
Ronny Eia eae6d3af5a Add Lua runtime: chip temp -> LED color -> HA, verified live
Sandboxed Lua state (espressif/lua v5.5.0, base/table/string/math only,
no io/os/package/debug) exposes a kvida API table (chip_temperature,
set_led, publish) to a compiled-in default script that reads chip
temperature, maps it to an LED color, and publishes the reading to
Home Assistant. Generalizes transport's publish_chip_temperature()
into publish_value(name, value), the first real use of AGENTS.md's
semantic publish API. Requires -DLUA_32BITS globally since this
toolchain's long long support isn't visible to Lua's default build.

Verified on real ESP32-C6 hardware: no crashes/errors across multiple
serial monitor windows, LED changes color, chip_temperature sensor
appears in Home Assistant via MQTT Discovery.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 09:17:06 +02:00
..

lua_runtime

Executes the sandboxed Lua program (compiled from Blockly) that defines device behaviour. Exposes the Kvida API (e.g. publish("temperature", 21.3)) to Lua scripts; Lua never touches ESP-IDF or transport directly. REQUIRES drivers profiles transport.

Built on espressif/lua (v5.5.0, pinned in idf_component.yml) -- Espressif's own official IDF Component Registry package, MIT licensed, built specifically for embedding Lua in ESP-IDF apps. No JS/exotic bindings: standard lua_State/luaL_newstate/lua_pushcfunction C API. The project's fourth external managed component (after mdns, mqtt, led_strip).

What's here

  • lua_runtime_init() creates the Lua state, opens only base/table/string/math (deliberately not luaL_openlibs(), which would also expose io, os, package/require, debug -- see AGENTS.md's "Lua should never access ESP-IDF directly"), registers the kvida API table, then loads the default script (src/default_script.h, a compiled-in string) which just defines on_tick().
  • lua_runtime_tick() calls on_tick() -- one "wake, execute Lua, publish changes, sleep" cycle. A Lua runtime error is logged, not fatal.
  • The kvida table exposed to scripts:
    • kvida.chip_temperature() -- reads the ESP32-C6's internal die temperature via profiles::ChipTemperatureSensor (the same Sensor implementation main.cpp used directly before this component existed).
    • kvida.set_led(r, g, b) -- sets the onboard WS2812 via drivers::rgb_led_set_color().
    • kvida.publish(name, value) -- calls transport::publish_value(), AGENTS.md's semantic publish API, built for the first time here.
  • The default script reads chip temperature, maps it to a blue(cool)-to-red(hot) LED color (linear interpolation, 20-60C, clamped), and publishes the reading to Home Assistant. Verified end-to-end on real hardware.

Known limitation, accepted for now

The onboard LED is also controllable manually from Home Assistant (an MQTT light entity, see components/transport/src/mqtt.cpp). The two aren't reconciled: on_tick() overwrites a manually-chosen HA color every 30s. Accepted as a conflict between two "quick proof" demos rather than solved now -- a real answer (e.g. a Lua-settable "mode" toggle, or the MQTT light entity deferring to Lua) needs product input, not a guess.

Not yet done

  • The script is compiled in, not loaded from storage -- there's no upload mechanism yet. Once kvida-sdk can push a script (and drivers mounts the storage LittleFS partition -- both still TODO), scripts should be loaded from there instead.
  • No Blockly-to-Lua compiler exists yet (that's kvida-sdk's job per AGENTS.md's "Level 2").
  • No memory/CPU/runtime limits on the Lua state beyond the restricted library set -- a script with an infinite loop would hang lua_runtime_tick() (and, since it currently runs on the same timer callback, block other esp_timer callbacks too). Not a concern for a compiled-in, developer-authored script; becomes one once arbitrary user scripts can be uploaded.