EVCC Lua 脚本手册¶
最后更新:
适用 EVCC:
v2.9.61按源码核对:2026-08-17 运行时:LuaJ;每个脚本拥有独立线程、全局环境、事件队列和日志缓冲
目录¶
- 1. 何时使用 Lua
- 2. 创建、目录与生命周期
- 3. 脚本头部元信息
- 4. 返回值与错误处理
- 5. 运行环境与安全边界
- 6. property:属性读写、枚举和监听
- 7. var:EVCC 变量
- 8. trigger:事件触发器
- 9. 动作模块
- 10. 通用工具模块
- 11. widget 与 float_btn
- 12. Web IDE
- 13. 完整示例
- 14. 性能、稳定性与排错
- 15. API 速查
1. 何时使用 Lua¶
Lua 适合:
- 同时监听多个车辆属性或变量;
- 需要累计、状态机、去抖、重试或持久化状态;
- 需要 HTTP、JSON、文件存储和车辆动作组合;
- 希望控制台按钮、悬浮按钮、自动化规则与脚本互相联动;
- 可视化自动化难以表达的复杂判断。
如果只是“一个触发器 + 几个条件 + 几个动作”,优先使用可视化自动化,维护和排错更简单。Lua 不是绕过 EVCC 权限、车型限制或服务连接状态的通道;它调用的车辆和系统能力仍依赖 EVCC 对应后端。
2. 创建、目录与生命周期¶
2.1 入口¶
打开顶部“脚本”页,切到“Lua 脚本”子页,可以:
- 新建或编辑
.lua文件; - 从剪贴板导入;
- 选择脚本根目录;
- 运行、停止或删除脚本;
- 开启/关闭自启动;
- 点击“触发”执行已经注册的手动回调;
- 打开 Web IDE;
- 进入脚本商店。
2.2 默认目录¶
首选根目录:
/sdcard/Download/EVCC/scripts/
├── lua/ Lua 脚本
├── frida/ Frida JavaScript
└── _data/ 每个 Lua 脚本的私有数据
如果公共目录不可写,会回退到应用自己的外部文件目录。用户在界面选择其他可写根目录后,选择结果会被保存。
脚本 ID 来自文件名,不含扩展名。例如:
文件名写入时会清理成字母、数字、点、下划线和短横线,最长 80 字符。
2.3 一次性脚本与常驻脚本¶
一次性脚本执行完主代码且没有注册任何事件处理器时自动退出:
注册 trigger.*、property.on_change()、var.on_change()、widget.on_press() 或 float_btn.on_press() 后,脚本会进入事件循环并保持运行:
每个脚本只会有一个运行实例。已停止或失败的实例可以重新运行。当前同时运行上限为 20 个。
2.4 启动和停止¶
--@autostart true的 Lua 脚本随自动化服务启动;多个自启脚本之间会错开约 200 毫秒;- 页面点击停止、
script.stop()、自动化服务退出或应用关闭都会请求停止; script.on_stop(fn)注册清理函数,停止时按注册的相反顺序执行;- 停止后最终日志保留约 60 秒,便于 Web IDE 拉取;
- 单脚本语法错误或运行异常只停止该脚本,不会直接停止其他脚本。
3. 脚本头部元信息¶
Lua 使用 --@字段 值,只扫描文件前 40 行:
--@name 音乐节奏灯
--@version 1.0.0
--@author EVCC User
--@description 播放时按 BPM 切换变量
--@min_evcc 2.9.61
--@permissions property_write, shell, http
--@autostart false
--@tags music, automation
| 字段 | 默认值 | 用途 |
|---|---|---|
name |
文件名 | 页面和日志中的名称 |
version |
0.0.0 |
脚本版本 |
author |
unknown |
作者 |
description |
空 | 功能说明 |
min_evcc |
空 | 声明最低 EVCC 版本 |
permissions |
空 | 逗号分隔的能力声明 |
autostart |
false |
是否自动启动 |
tags |
空 | 逗号分隔标签 |
重要边界:
permissions只是供脚本商店和用户审阅的声明,不是运行时权限系统;min_evcc是元信息,不应替代脚本自身的兼容判断;- 第三方脚本即使没有声明
shell或property_write,仍应人工检查源码; - 不要在元信息或源码中写入 Token、密码、家庭坐标等敏感信息。
4. 返回值与错误处理¶
多数需要访问后端的 API 使用统一约定:
local value, err = property.read("vhal:VehicleSpeed")
if not value then
log.e("读取失败", err)
return
end
成功:
失败:
写入和动作通常返回:
property、var、shell、http、json、storage、time.parse 和动作模块的错误会自动进入当前脚本日志;脚本仍可读取 err 自定义处理。同一脚本 5 秒内完全相同的 API 错误会被日志节流。
少数 API 的返回方式不同:
log.*、sleep()、toast.show()、trigger.off()没有结构化成功返回;var.delete()、storage.exists()返回布尔值;var.list()、storage.list()、automation.list()直接返回 Lua 表;widget.on_press()、float_btn.on_press()直接返回处理器 ID 或nil。
5. 运行环境与安全边界¶
5.1 可用的 Lua 能力¶
标准 Lua 的基础语法、math、string、table 等常用库可用。print(...) 会按制表符拼接参数并重定向到 log.i(...)。
os 只保留:
5.2 被移除的能力¶
EVCC 显式移除:
io
package
require
loadfile
dofile
debug
os.execute
os.exit
os.remove
os.rename
os.tmpname
os.getenv
os.setlocale
需要命令、HTTP 或文件存储时,使用文档中的 shell、http、storage 模块。
5.3 这不是不可信代码隔离容器¶
Lua 环境降低了常见误操作风险,但 EVCC 本身拥有 RootService、车辆属性写入和系统动作能力,不能把脚本沙箱视为可以安全运行任意陌生代码的安全边界。
安装第三方脚本前至少检查:
- 是否调用
shell.exec(); - 是否写车辆属性、系统设置或系统属性;
- 是否向外部 URL 上传数据;
- 是否包含死循环、频繁定时器或高频日志;
- 是否包含密钥、Token、手机号、VIN 或坐标。
5.4 循环与停止响应¶
sleep(ms) 每 100 毫秒检查一次停止请求,阻塞等待可较快退出。但当前 LuaJ 没有真正的逐指令中断钩子,纯 Lua 忙循环可能无法及时停止:
需要循环时必须让出执行:
更推荐使用 trigger.on_timer(),让脚本进入事件循环。
6. property:属性读写、枚举和监听¶
6.1 来源前缀¶
| 前缀 | 别名 | 数据源 |
|---|---|---|
vhal: |
省略前缀时默认 | VHAL |
apvp: |
— | APVP |
aidl: |
— | 车机 AIDL 属性源 |
capi: |
carapi: |
CAPI / CarApi |
adaptapi: |
adapt: |
AdaptAPI |
soa: |
— | SOA |
settings: |
setting: |
Android Settings |
sysprop: |
— | Android System Property |
6.2 读取¶
local speed, err = property.read("vhal:VehicleSpeed")
local gear = property.read("apvp:561024570")
local aidl = property.read("aidl:557877498")
local capi = property.read("capi:557877498/0")
local adapt = property.read("adaptapi:772277248")
local brightness = property.read("settings:system/screen_brightness")
local sdk = property.read("sysprop:ro.build.version.sdk")
寻址规则:
- VHAL 可使用已知名称、十进制 ID 或
十进制ID/区域ID;VHAL 读取当前不接受0xID; - APVP 可使用信号名称或十进制 ID;
- AIDL、CAPI、AdaptAPI、SOA 可使用十进制 ID、
0xID、ID/区域ID、名称或当前缓存中的十六进制标识; settings:必须是system/key、secure/key或global/key;sysprop:后面直接写属性键。
车辆属性的返回值会去掉显示后缀。例如底层显示:
Lua property.read() 返回:
因此可以直接 tonumber() 或做等值判断。settings: 和 sysprop: 保持系统返回的原始字符串。
6.3 写入¶
local ok, err = property.write("vhal:SomeProperty", "1")
local ok2, err2 = property.write("aidl:557877498/0", "42")
local ok3, err3 = property.write("settings:system/screen_brightness", "180")
local ok4, err4 = property.write("sysprop:debug.evcc.demo", "1")
写入依赖对应后端已经连接、属性元数据可用且属性允许写入。请先在属性页确认含义、数据类型和可写性。错误的车辆属性值可能影响车辆功能;不要在不了解属性用途时试写。
6.4 枚举¶
local all = property.list()
local vhal = property.list("vhal")
local aidl = property.list("aidl")
local capi = property.list("capi")
local adapt = property.list("adaptapi")
返回从下标 1 开始的字符串数组。AIDL、CAPI、AdaptAPI 和 SOA 列表以十进制属性 ID 为主,例如:
列表来自当前已连接后端的缓存,不保证后端未连接时仍有内容。
6.5 元数据¶
local info, err = property.info("aidl:557877498")
if info then
log.i(info.source, info.id, info.area_id, info.value)
end
车辆属性返回字段:
| 字段 | 说明 |
|---|---|
source |
vhal / apvp / aidl / capi / adaptapi |
id |
十进制属性 ID |
area_id |
区域 ID |
name |
属性名称 |
value |
规整后的原始值 |
value_display |
底层完整展示值 |
data_type |
数据类型说明 |
status |
当前状态 |
timestamp |
属性时间戳 |
settings: 和 sysprop: 的 info 只返回 source 和 name。
6.6 监听变化¶
local handler, err = property.on_change(
"aidl:557877498/0",
function(p)
log.i("新值", p.new, "旧值", p.old)
end,
{cooldown_ms = 500}
)
第三个参数可选:
| 参数 | 默认 | 说明 |
|---|---|---|
cooldown_ms |
0 |
两次回调的最短间隔 |
negate |
false |
反转匹配结果;属性事件通常不建议使用 |
回调 p 字段:
只写 aidl:<ID> 时匹配该 ID 的任意区域;确实需要区分区域时写 aidl:<ID>/<areaId>。APVP 监听会自动为该脚本添加订阅,脚本停止时自动注销。
相同值的底层重复上报、或界面/脚本主动刷新当前值,不会被当作真实变化重复回调。
7. var:EVCC 变量¶
7.1 读取与写入¶
Lua 与自动化、公式、小程序 MQTT 和 AIDL 联动共用变量。值按字符串读写;需要计算时自行转换:
7.2 内置变量¶
时间、GPS 和音乐预设变量可直接读取:
var.read("time")
var.read("timestamp")
var.read("gps_available")
var.read("gps_lat")
var.read("bpm")
var.read("music_state")
音乐变量:
| 名称 | 值 |
|---|---|
bpm |
BPM 字符串;无音乐、未匹配或匹配失败时为 "0",暂停时保留当前歌曲 BPM |
music_state |
playing / paused_or_stopped / none |
bpm 和 music_state 是只读变量。对它们调用 var.write() 会返回错误,var.delete() 返回 false。
7.3 列表和删除¶
var.list() 只列出用户创建的自定义变量,不列出时间、GPS、BPM 等预设变量。
7.4 监听¶
local id, err = var.on_change("music_state", function(p)
log.i("music_state", p.old, "->", p.new)
end, {cooldown_ms = 200})
回调字段:
第三个参数支持 cooldown_ms 和 negate。一般变量监听不要使用 negate,否则其他变量的变化也可能成为反向匹配结果。
8. trigger:事件触发器¶
所有注册成功的处理器都会返回 ID。可随时取消:
8.1 触发器总表¶
| API | 匹配参数 | 回调 payload |
|---|---|---|
trigger.on_boot(fn) |
无 | 空表 |
trigger.on_screen(opts, fn) |
on、cooldown_ms、negate |
on |
trigger.on_app_foreground(pkg, fn) |
包名 | pkg、foreground=true |
trigger.on_app_background(pkg, fn) |
包名 | pkg、foreground=false |
trigger.on_broadcast(action, fn) |
Intent action | action |
trigger.on_log(opts, fn) |
tag、keyword、regex、cooldown_ms、negate |
tag、keyword、line |
trigger.on_ble(opts, fn) |
preset、button、mfg_id、mfg_data_prefix、冷却/反转 |
mac、mfg_id、mfg_data、preset、button |
trigger.on_bluetooth(opts, fn) |
on、冷却/反转 |
on |
trigger.on_bt_device(opts, fn) |
mac、event、冷却/反转 |
mac、name、connected |
trigger.on_hard_key(opts, fn) |
code、action、冷却/反转 |
key_code、action |
trigger.on_geofence(opts, fn) |
mode、name、冷却/反转 |
name、entered、lat、lng |
trigger.on_automation(idOrName, fn) |
规则 ID 或名称 | rule_id、rule_name |
trigger.on_timer(ms, fn) |
周期间隔,最短 50 ms | 无参数 |
trigger.on_cron("HH:mm", fn) |
每天本地时间 | 无参数 |
trigger.on_sun(opts, fn[, extraOpts]) |
日出/日落与偏移 | event、offset、target_ms |
trigger.on_manual(fn) |
页面、控制台、悬浮窗或自动化手动触发 | 无参数 |
8.2 常用示例¶
trigger.on_screen({on = true, cooldown_ms = 2000}, function(p)
log.i("亮屏", p.on)
end)
trigger.on_app_foreground("com.example.music", function(p)
log.i("音乐应用进入前台", p.pkg)
end)
trigger.on_log({tag = "Media", keyword = "ERROR", cooldown_ms = 5000}, function(p)
log.w(p.line)
end)
trigger.on_log({regex = "speed=([0-9]+)"}, function(p)
local speed = string.match(p.line, "speed=([0-9]+)")
log.i("speed", speed or "?")
end)
trigger.on_ble({preset = "rotary_1", button = 3, mfg_id = 10262}, function(p)
log.i("BLE 按键", p.button, p.mfg_data)
end)
trigger.on_bt_device({mac = "AA:BB:CC:DD:EE:FF", event = "connect"}, function(p)
log.i("蓝牙设备已连接", p.name)
end)
trigger.on_hard_key({code = 24, action = "pressed"}, function(p)
log.i("音量加按下", p.key_code)
end)
trigger.on_geofence({name = "家", mode = "enter"}, function(p)
log.i("进入", p.name, p.lat, p.lng)
end)
8.3 定时、日出日落和手动触发¶
trigger.on_timer(60000, function()
log.i("每分钟")
end)
trigger.on_cron("08:30", function()
tts.speak("早上好")
end)
trigger.on_sun({event = "sunset", offset = -15, cooldown_ms = 60000}, function(p)
log.i("日落前 15 分钟", p.target_ms)
end)
trigger.on_manual(function()
notification.show("EVCC", "脚本已手动触发")
end)
on_sun 的 offset 单位为分钟,负数表示提前,正数表示延后;每 30 秒检查一次。无法取得定位时当前实现使用北京坐标作为兜底,因此对地理位置敏感的脚本应先自行检查 gps_available。
也兼容把冷却放在第三个参数:
trigger.on_sun(
{event = "sunrise", offset = 0},
function(p) log.i(p.event) end,
{cooldown_ms = 60000}
)
9. 动作模块¶
动作模块复用 EVCC 自动化动作执行器。除特别注明外,返回 (true, nil) 或 (nil, err)。
9.1 应用与系统导航¶
app.launch("com.example.app")
app.launch("com.example.app", 2) -- 可选 display ID
app.kill("com.example.app")
app.background("com.example.app", true) -- true 时同时回桌面
home.press()
voice_assistant.dismiss()
9.2 媒体和音量¶
第一个参数是车机音量组的数字 ID,不是 "media" 等名称。可先在服务页“音量组调整”查看和测试本车音量组。
9.3 播放声音与 TTS¶
audio.play("/sdcard/Music/notice.mp3", {
usage = 11,
output = "usage",
bus_address = "",
repeat = false,
repeat_count = 0
})
tts.speak("注意减速", {
engine = "",
voice = "",
usage = -1,
output = "usage",
bus_address = "",
repeat = true,
repeat_count = 2
})
tts.stop() 当前没有接通可靠的取消动作,只是兼容占位;脚本不要依赖它停止正在播放的 TTS。
9.4 广播与通知¶
broadcast.send("com.example.ACTION", {
mode = "demo",
count = 3
})
notification.show("EVCC", "任务完成")
广播 extras 会被转换为字符串键值。不要用它传复杂嵌套对象。
9.5 BLE、远程消息和 Webhook¶
ble.broadcast("AABBCCDD")
remote.send("车辆状态提醒")
webhook.post("https://example.com/hook", "{\"ok\":true}")
ble.broadcast() 只暴露基础十六进制载荷;需要广播间隔、协议或复杂参数时,使用可视化自动化或 BLE 广播台配置。
9.6 自动化互调¶
local rules = automation.list()
for _, rule in ipairs(rules) do
log.i(rule.id, rule.name, rule.enabled)
end
automation.trigger("rule_id_or_name")
Lua 触发自动化、自动化再触发 Lua 可能形成递归。automation.trigger() 当前最多允许 5 层调用深度,超出返回错误。设计互调时仍应使用状态变量或冷却避免循环。
9.7 小窗和异屏同显¶
mini_window.launch("com.example.app")
system_mini_window.launch("com.example.app")
display_mirror.start(2)
display_mirror.stop()
这些动作是否成功取决于车型、目标显示屏和相应服务状态。
10. 通用工具模块¶
10.1 log 与 print¶
日志同时进入当前脚本缓冲和 EVCC 全局日志。每个脚本最多保留 2000 行,超出后丢弃最早内容。高频回调中不要每次都打印。
10.2 sleep¶
单位为毫秒;负数不会等待。长等待按约 100 毫秒切片,以便响应停止请求。
10.3 shell¶
local out, err = shell.exec("getprop ro.product.model")
local longOut, longErr = shell.exec("some_long_command", 60000)
命令通过 RootService 执行并同步阻塞当前脚本线程。默认按普通命令路径执行;传入超过 30 秒的超时时间会使用长任务路径。不要在高频触发器里重复执行耗时命令,也不要拼接未经验证的外部输入。
10.4 toast¶
Toast 依赖 RootService。该 API 没有结构化错误返回,失败会写警告日志。
10.5 http¶
local resp, err = http.get("https://example.com/status", {
headers = {Authorization = "Bearer token"},
timeout_ms = 10000,
follow_redirects = true
})
local resp2, err2 = http.post(
"https://example.com/api",
"a=1&b=2",
{timeout_ms = 10000}
)
local resp3, err3 = http.request({
method = "PUT",
url = "https://example.com/api/item",
body = "{\"enabled\":true}",
headers = { ["Content-Type"] = "application/json" },
timeout_ms = 15000,
follow_redirects = false
})
响应:
规则:
- 请求在脚本线程同步执行;
- 默认连接和读取超时 30 秒,最短 1 秒;
- 默认跟随重定向;
- POST/PUT/PATCH/DELETE 有 body 且未指定 Content-Type 时默认为
application/x-www-form-urlencoded; - 返回体最多保留前 8 MiB;
- 非 2xx 状态仍会返回
status和错误响应体,不会仅因 HTTP 状态码而返回err。
10.6 json¶
local text, err = json.encode({name = "EVCC", enabled = true})
local obj, decodeErr = json.decode('{"value":42}')
连续的 1..n 整数键表编码为 JSON 数组,否则编码为对象:
10.7 storage¶
每个脚本拥有独立目录:
storage.write("state.json", "{\"count\":1}")
local text, err = storage.read("state.json")
local exists = storage.exists("state.json")
local files = storage.list()
local ok, deleteErr = storage.delete("state.json")
允许相对的子目录,例如 cache/state.json。绝对路径、反斜杠、任何包含 .. 的路径和越出脚本数据目录的路径会被拒绝。storage.list() 只列出脚本数据目录第一层的文件或目录名。
10.8 time¶
local now = time.now()
local text = time.format(now, "yyyy-MM-dd HH:mm:ss")
local ts, err = time.parse("2026-08-17 12:30:00", "yyyy-MM-dd HH:mm:ss")
local today = time.today()
time.today() 返回:
weekday 为周一 1 到周日 7。格式使用 Java SimpleDateFormat,不是 Lua os.date 格式。
10.9 script¶
log.i(script.id)
log.i(script.name)
log.i(script.version)
log.i(script.author)
log.i(script.path)
log.i(script.started_at)
script.on_stop(function()
log.i("清理")
end)
-- script.stop()
started_at 是 EVCC 单调时钟毫秒值,适合计算本次运行时长,不是 Unix 时间戳。
11. widget 与 float_btn¶
监听控制台组件点击:
监听悬浮按钮:
当前只有“监听点击”方向已接通。以下接口仍是占位,不会执行组件原动作:
需要从 Lua 主动执行动作时,请直接调用对应动作模块,或使用 automation.trigger()。
12. Web IDE¶
12.1 打开方式¶
脚本页点击“Web IDE”,EVCC 会显示二维码和局域网地址:
Web IDE 当前没有独立登录认证,只应在可信局域网临时开启。不要通过路由器端口映射、frp 或公网隧道暴露 29999 端口。
12.2 主要能力¶
- Lua / Frida 编辑模式切换;
- 选择、加载和保存本地脚本;
- 运行、停止 Lua;
- 拉取实时和最终日志;
- 复制、清空、自动滚动和暂停日志;
- 为调试运行注入属性和变量 mock。
属性 mock 示例:
变量 mock 示例:
mock 只影响 Web IDE 本次临时 Lua 实例,不写真实车辆属性或全局变量。当前临时运行只支持 Lua;Frida 可以编辑和保存,但不通过同一临时运行接口执行。
13. 完整示例¶
13.1 BPM 与音乐状态¶
--@name BPM 状态演示
--@version 1.0.0
--@min_evcc 2.9.61
--@autostart true
--@permissions variables, notification
local function update()
local bpm = tonumber(var.read("bpm")) or 0
local state = var.read("music_state") or "none"
if state == "playing" and bpm >= 120 then
var.write("music_energy", "high")
elseif state == "playing" then
var.write("music_energy", "normal")
else
var.write("music_energy", "idle")
end
log.i("state=" .. state .. " bpm=" .. bpm)
end
var.on_change("bpm", update, {cooldown_ms = 200})
var.on_change("music_state", update, {cooldown_ms = 200})
update()
13.2 属性阈值提醒¶
--@name 车速提醒
--@version 1.0.0
--@permissions property_read, tts
local warned = false
property.on_change("vhal:VehicleSpeed", function(p)
local speed = tonumber(p.new)
if not speed then return end
if speed >= 120 and not warned then
warned = true
tts.speak("当前车速较高,请注意安全")
elseif speed < 110 then
warned = false
end
end, {cooldown_ms = 300})
实际车速属性的单位和 ID因车型而异,先在属性页确认。
13.3 持久化计数¶
local raw = storage.read("count.txt")
local count = tonumber(raw) or 0
trigger.on_manual(function()
count = count + 1
storage.write("count.txt", tostring(count))
toast.show("已触发 " .. count .. " 次")
end)
storage.read() 失败时第一个返回值为 nil,这里使用 tonumber(nil) or 0 安全回退。
13.4 HTTP JSON 获取与变量更新¶
trigger.on_timer(60000, function()
local resp, err = http.get("https://example.com/evcc.json", {
timeout_ms = 10000
})
if not resp then
log.w("请求失败", err)
return
end
if resp.status ~= 200 then
log.w("HTTP", resp.status)
return
end
local body, jsonErr = json.decode(resp.body)
if not body then
log.w("JSON 失败", jsonErr)
return
end
if body.mode ~= nil then
var.write("cloud_mode", tostring(body.mode))
end
end)
不要在脚本中硬编码长期 Token。可由用户手动写入 EVCC 变量或脚本私有 storage,并避免上传到脚本商店。
13.5 清理处理器¶
local timerId = trigger.on_timer(10000, function()
log.i("tick")
end)
trigger.on_manual(function()
trigger.off(timerId)
log.i("定时器已取消")
end)
script.on_stop(function()
log.i("脚本结束")
end)
14. 性能、稳定性与排错¶
14.1 建议¶
- 车辆属性变化可能非常频繁,优先设置
cooldown_ms; - 不要在每次属性回调中执行 Shell、HTTP 或大量 JSON;
- 定时器通常不要低于 500~1000 毫秒,除非确有需要;
- 需要保存高频状态时先留在 Lua 变量,低频批量写
storage; - 先在 Web IDE 使用 mock 验证判断,再连接真实属性;
- 车辆写入先用只读日志验证触发条件,确认后再加入
property.write(); - 为互相触发的自动化与脚本设置状态变量和冷却,避免循环。
14.2 脚本一运行就结束¶
主代码执行完后没有注册处理器。需要常驻时至少注册一个:
不要用空的 while true 维持常驻。
14.3 提示后端未连接¶
例如:
检查:
- 设置页对应属性源开关是否开启;
- 属性页是否显示连接成功;
- 脚本是否早于服务连接完成就执行;
- 是否应在定时器中稍后重试。
14.4 on_change 不触发¶
- 确认来源前缀正确;
- 确认属性 ID、名称和区域 ID 与事件一致;
- APVP 会自动订阅,但上游仍需连接;
- 相同值重复刷新按设计不触发;
- 检查是否被
cooldown_ms限制; - 不要误用
negate=true。
14.5 停止按钮没有立即生效¶
常见原因是纯 Lua 忙循环,或正在同步等待 Shell / HTTP。循环加入 sleep(),耗时工作降低频率并设置合理超时。
14.6 中文变成问号¶
当前引擎按 UTF-8 加载脚本。请确认文件保存为 UTF-8;从其他编辑器复制时避免使用异常编码。
14.7 Web IDE 无法打开¶
- 手机/电脑和车机需要在可互访的局域网;
- 使用页面显示的实际 IP 和端口
29999; - 检查热点/路由器是否开启客户端隔离;
- 不要把服务暴露公网来绕过局域网问题。
15. API 速查¶
property.read(name)
property.write(name, value)
property.list([source])
property.info(name)
property.on_change(name, fn[, opts])
var.read(name)
var.write(name, value)
var.list()
var.delete(name)
var.on_change(name, fn[, opts])
trigger.on_boot(fn)
trigger.on_screen(opts, fn)
trigger.on_app_foreground(pkg, fn)
trigger.on_app_background(pkg, fn)
trigger.on_broadcast(action, fn)
trigger.on_log(opts, fn)
trigger.on_ble(opts, fn)
trigger.on_bluetooth(opts, fn)
trigger.on_bt_device(opts, fn)
trigger.on_hard_key(opts, fn)
trigger.on_geofence(opts, fn)
trigger.on_automation(idOrName, fn)
trigger.on_timer(ms, fn)
trigger.on_cron("HH:mm", fn)
trigger.on_sun(opts, fn[, extraOpts])
trigger.on_manual(fn)
trigger.off(handlerId)
app.launch(pkg[, displayId])
app.kill(pkg)
app.background(pkg[, toHome])
home.press()
voice_assistant.dismiss()
media.play_pause()
media.next()
media.previous()
media.volume(groupId, value)
audio.play(path[, opts])
tts.speak(text[, opts])
broadcast.send(action[, extras])
notification.show(title, text)
ble.broadcast(hex)
remote.send(text)
webhook.post(url, body)
automation.trigger(idOrName)
automation.list()
mini_window.launch(pkg)
system_mini_window.launch(pkg)
display_mirror.start(displayId)
display_mirror.stop()
log.i(...)
log.w(...)
log.e(...)
log.clear()
sleep(ms)
shell.exec(command[, timeoutMs])
toast.show(text[, long])
http.get(url[, opts])
http.post(url, body[, opts])
http.request(opts)
json.encode(value)
json.decode(text)
storage.read(path)
storage.write(path, content)
storage.delete(path)
storage.list()
storage.exists(path)
time.now()
time.format([timestamp[, pattern]])
time.parse(text[, pattern])
time.today()
script.stop()
script.on_stop(fn)
widget.on_press(id, fn)
float_btn.on_press(id, fn)
16. 源码索引¶
| 内容 | 文件 |
|---|---|
| 脚本生命周期 | ScriptEngine.kt、ScriptInstance.kt |
| Lua 环境和模块注册 | ScriptRuntime.kt |
| 元信息和目录 | ScriptMeta.kt、ScriptRepository.kt |
| 事件 payload | ScriptEngineEvent.kt、EventBridge.kt |
| 各 API 实现 | scriptengine/modules/ |
| Web IDE | scriptengine/debug/ |
相关文档:
Geely 属性引用¶
新增前缀 geely_aidl:、geely_capi:。使用完整引用,例如 geely_aidl:signal:0x4061/1、geely_capi:sensor:0x100100/0。AIDL 通道为 signal/pa,CAPI 为 function/sensor。目录名称是通道限定名称,区域跟在 / 后。只给数字 ID 时必须匹配唯一属性。
读取、目录、信息、监听和支持的写入沿用 property 模块现有方法。Sensor 原生只读;写入使用目录声明的类型,字节值显式使用 base64:。显式选源失败不会换来源或提升调用身份。手动读取和首次快照不触发变化事件。