How To Call It
Trigger is a global table: call its functions with a dot (Trigger.<method>(...)), not a colon. Requires Macro Handler 1.0.60 or later.
Registers an observer without blocking the main flow. Event actions run one at a time at safe checkpoints on the same Lua thread, then the main flow resumes from its saved position. Trigger.join cannot be called inside an event handler. Reusing an active name returns the existing observer.
Documented for app version 1.0.59Updated:
Trigger is a global table: call its functions with a dot (Trigger.<method>(...)), not a colon. Requires Macro Handler 1.0.60 or later.
In automation flow, chain actions with short wait() intervals and use requestStop() or controlled retry on failure paths.
Required: options, handlers. Optional: name (omit to pass nil and use the runtime default). Returns a Lua table.
events contains at least one event. packageName filters apps/notifications and textContains filters notification text. timeoutMs: 0–86400000 (default 60000; 0 until stopped), intervalMs: 50–60000 (250), cooldownMs: 0–86400000 (1000), maxEventAgeMs: 100–86400000 (30000), maxEvents: 0–1000000 (1; 0 unlimited). Combine constraints key/operator/value entries with constraintMode="all", "any" or "none". Unknown state cannot pass even a not-equal constraint. condition requires a check function and fires on false-to-true transitions; fireInitially=true also accepts the first true state. Event names: app_foreground, app_background, network_connected, network_disconnected, network_changed, internet_available, internet_lost, vpn_connected, vpn_disconnected, proxy_changed, screen_on, screen_off, device_unlocked, charger_connected, charger_disconnected, battery_changed, power_save_changed, headset_changed, orientation_changed, notification_posted, notification_removed, interval, condition, wifi_connected, wifi_disconnected, cellular_connected, cellular_disconnected, metered_changed, battery_low, battery_okay, time_changed, timezone_changed, charging_started, charging_stopped, headset_connected, headset_disconnected. State keys: connected, validated, vpn, proxy, screenOn, unlocked, charging, powerSave, battery, foregroundPackage, orientation, wifi, cellular, ethernet, metered, roaming, wiredHeadset, batteryTemperature, localTime, weekday, inputIdle. Operators are eq/ne, plus gt/lt/ge/le for numeric fields. Boolean values are true/false, orientation is portrait/landscape, battery is 0–100. Notification text filter is at most 512 UTF-16 units and is case-insensitive. constraintGroups supports up to four nested ALL/ANY/NONE levels and 64 total groups and conditions. source="event" reads event fields; default source="state" reads device state. String operators match literal case-sensitive text. Numbers accept gt/lt/ge/le/eq/ne; localTime uses inside/outside with HH:mm-HH:mm, including overnight windows. weekday is Monday=1 through Sunday=7; batteryTemperature is Celsius. priority is 0–100, higher first. Trigger.watch defaults omitted timeoutMs and maxEvents to zero. Actions wait until held input is released.
Observation lasts only while the macro runs; this is not a persistent background scheduler. At most 8 subscriptions and 128 events per subscription are allowed; overflow raises an error. Stop and run completion release listeners. App events require connected accessibility; notification events require connected notification access. VPN describes the default network visible to MacroHandler and proxy describes Android HTTP/PAC configuration; neither proves tunnel health or the traffic of other apps. For long-running coded observers put -- @mh:user-infinite-loop at the start of the script; visual blocks add it automatically. Without it the experimental native engine retains a 10-minute hard deadline. The directive preserves stop, heap and continuous active-code protections.
The snippet below is a starter pattern that can be applied directly in runtime flow.
-- @mh:user-infinite-loop
local observer = Trigger.watch({events={"vpn_disconnected", "internet_lost"}}, {
action=function(event)
System.postNotification("Connection changed", event.type)
end
}, "network_guard")
-- The macro can run other groups here.
wait(5000)
print(Trigger.status(observer).count)
Trigger.close(observer)From foundation to combined usage, each level is provided as a separate code block so you can copy the level you need and adapt it directly.
-- @mh:user-infinite-loop
local observer = Trigger.watch({events={"vpn_disconnected", "internet_lost"}}, {
action=function(event)
System.postNotification("Connection changed", event.type)
end
}, "network_guard")
-- The macro can run other groups here.
wait(5000)
print(Trigger.status(observer).count)
Trigger.close(observer)local stepOk = true
-- @mh:user-infinite-loop
local observer = Trigger.watch({events={"vpn_disconnected", "internet_lost"}}, {
action=function(event)
System.postNotification("Connection changed", event.type)
end
}, "network_guard")
-- The macro can run other groups here.
wait(5000)
print(Trigger.status(observer).count)
Trigger.close(observer)
if stepOk then
wait(200)
endlocal ok, err = pcall(function()
-- @mh:user-infinite-loop
local observer = Trigger.watch({events={"vpn_disconnected", "internet_lost"}}, {
action=function(event)
System.postNotification("Connection changed", event.type)
end
}, "network_guard")
-- The macro can run other groups here.
wait(5000)
print(Trigger.status(observer).count)
Trigger.close(observer)
end)
if not ok then
print("API step failed: Trigger.watch: " .. tostring(err))
requestStop()
end-- In automation flow, chain actions with short wait() intervals and use requestStop() or controlled retry on failure paths.
local function run_watch_step()
-- @mh:user-infinite-loop
local observer = Trigger.watch({events={"vpn_disconnected", "internet_lost"}}, {
action=function(event)
System.postNotification("Connection changed", event.type)
end
}, "network_guard")
-- The macro can run other groups here.
wait(5000)
print(Trigger.status(observer).count)
Trigger.close(observer)
end
local ok, err = pcall(run_watch_step)
if not ok then
toast("Step failed")
print(err)
end-- @mh:user-infinite-loop
local observer = Trigger.watch({events={"vpn_disconnected", "internet_lost"}}, {
action=function(event)
System.postNotification("Connection changed", event.type)
end
}, "network_guard")
-- The macro can run other groups here.
wait(5000)
print(Trigger.status(observer).count)
Trigger.close(observer)