Skip to content
aaalllexxx edited this page Jun 29, 2026 · 1 revision

Events

ENPAF has a thread-safe pub/sub system. Events flow in both directions:

  • Python → Python: @app.on(event) handlers.
  • Python → JS: app.emit(event, data) also pushes to the WebView.
  • JS → Python: enpaf.emit(event, data) fires Python handlers.
  • JS ← Python: enpaf.on(event, handler) receives pushed events.

Subscribing (Python)

@app.on("app_start")
def on_start():
    print("started")

@app.on("wifi_scan_result")
def on_net(net):
    print(net["ssid"], net.get("rssi"))

Lower-level app.events API:

app.events.on(event, handler)       # subscribe
app.events.once(event, handler)     # fire once, then auto-remove
app.events.off(event, handler)      # remove one
app.events.off(event)               # remove all for event
app.events.emit(event, *args)       # Python handlers ONLY (no JS)
app.events.has_listeners(event)     # bool
app.events.listener_count(event)    # int
app.events.event_names()            # list of active events

⚠️ app.emit vs app.events.emitapp.emit(event, data) fires Python handlers and pushes to JS. app.events.emit(...) fires Python handlers only. If your UI isn't receiving an event, you probably used the latter.

Emitting

# Python: reaches both Python handlers and JS
app.emit("download_progress", {"percent": 42})
// JS: reaches Python @app.on handlers
enpaf.emit("button_clicked", { id: "save" });

// JS: receive events pushed from Python
enpaf.on("download_progress", (d) => setBar(d.percent));

Error isolation

A handler that raises does not stop the others. Instead the emitter fires an app_error event with the exception, the offending event name, and the handler. (The app_error path itself is guarded against infinite recursion.)

@app.on("app_error")
def on_error(exc, event=None, handler=None):
    print("handler failed:", exc)

Lifecycle events

Built-in events the framework emits:

Event When
app_start App has started.
app_stop App is stopping.
app_pause / app_resume Activity paused / resumed (Android).
app_error A handler raised an exception.
page_load / page_unload A page loaded / is unloading (sent from JS).
bridge_connect / bridge_disconnect Bridge connection state.

Capability & system events

Pushed by the device modules and the Android runtime. Subscribe with @app.on(...) (Python) or enpaf.on(...) (JS):

Event Payload Source
wifi_scan_result one network {ssid, rssi, ...} Wi-Fi
wifi_scan_finished Wi-Fi
wifi_connected / wifi_error status Wi-Fi
bluetooth_device_found {name, address, ...} Bluetooth
bluetooth_connected / bluetooth_disconnected status Bluetooth
bluetooth_data received bytes/text Bluetooth
bluetooth_discovery_finished / bluetooth_error Bluetooth
nfc_tag {id, from_launch} NFC (tag tapped)
nfc_write_result {written, id, ...} NFC (armed write done)
nfc_open {uri} NFC (auto-open on launch)
permission_result {code, granted:[...], denied:[...], results:{...}} Permissions
biometric_result {success, error} Biometric
install_status {status, success, message} PackageInstaller (Companion)
activity_result {request_code, result_code, data} onActivityResult

Example: live Wi-Fi scan

# Python side just logs; the UI gets each result too
@app.on("wifi_scan_result")
def _log(net):
    print("found", net.get("ssid"))
enpaf.wifi.onResult((n) => addRow(n.ssid, n.rssi));
enpaf.wifi.onFinished(() => console.log("scan done"));
enpaf.wifi.scan();

Clone this wiki locally