Skip to content

Python API

aaalllexxx edited this page Jun 29, 2026 · 1 revision

Python API

Everything you write in main.py revolves around one class: EnpafApp.

from enpaf import EnpafApp
app = EnpafApp(__name__)

EnpafApp(import_name, app_dir="app", config_file="enpaf.json")

Arg Default Description
import_name Usually __name__. Used to locate the project directory.
app_dir "app" Directory with your HTML/CSS/JS, relative to the project.
config_file "enpaf.json" Project config file.

On construction the app loads enpaf.json, detects whether it's running on Android, opens SQLite storage, wires up the bridge/events/router/device-API, and instantiates all capability modules.

Properties

Property Description
app.config The parsed enpaf.json as a dict.
app.name The app name (config["name"], default "ENPAF App").
app.bridge The Bridge (Python ↔ JS).
app.events The EventEmitter.
app.router The Router (routes + Jinja2 rendering).
app.storage The Storage (SQLite kv + collections).
app.api The DeviceAPI (sensors, permissions, toasts, …).
app.<module> Capability modules: wifi, bluetooth, location, sensors, nfc, audio, battery, notifications, device, permissions, media, biometric. See Device Capabilities.

Decorators

@app.route(path, methods=None)

Register a page route. The handler returns HTML (typically via app.render).

@app.route("/")
def index():
    return app.render("index.html", title=app.name)

@app.route("/about")
def about():
    return app.render("pages/about.html")

On-device the WebView loads static assets directly, so routing/rendering mainly matters for the dev server. Keep page logic in bridge handlers so it behaves the same in both environments.

@app.bridge_handler(name) (alias @app.bridge_func)

Expose a Python function to JavaScript. The handler receives a single params dict and returns any JSON-serializable value.

@app.bridge_handler("get_user")
def get_user(params):
    uid = params.get("id")
    return {"id": uid, "name": "Alex"}
const user = await enpaf.call("get_user", { id: 1 });

Exceptions are caught and returned to JS as a rejected promise with the error message — they never crash the app.

@app.on(event)

Register an event listener (Python side). Fires for lifecycle events, module events, and anything you emit.

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

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

See the full event list in Events.


Methods

app.emit(event, data=None)

Emit an event to both Python handlers and the JavaScript side.

app.emit("progress", {"percent": 42})
enpaf.on("progress", (d) => console.log(d.percent));

⚠️ Use app.emit(...), not app.events.emit(...), when you want JS to receive the event. app.events.emit only fires Python handlers and never reaches the WebView.

app.render(template_name, **context)

Render a Jinja2 template from app_dir and inject the bridge <script>. The app name and config are added to the context automatically.

return app.render("index.html", title="Home", items=items)

app.run(host="127.0.0.1", port=8080, debug=False, open_browser=True)

Start the app. On Android it launches the WebView runtime; on desktop it starts the Flask + Socket.IO dev server. Normally called from the __main__ guard:

if __name__ == "__main__":
    app.run()

Low-level components

You rarely need these directly, but they're available:

Bridge (app.bridge)

app.bridge.register("name", handler)        # like @app.bridge_handler
app.bridge.unregister("name")
app.bridge.get_registered_handlers()        # -> ["name", ...]
app.bridge.emit_to_js(event, data)          # push to JS only
app.bridge.on_js_event(event, handler)      # handle events coming from JS

The bridge transparently uses Socket.IO (with an HTTP fallback) in dev and the Android JavaScript interface on-device. See Architecture.

DeviceAPI (app.api)

Direct device access from Python (works with dev stubs off-device):

app.api.vibrate(200)
app.api.toast("Saved")
loc = app.api.get_location()            # {latitude, longitude, ...}
batt = app.api.get_battery()            # {level, charging}
acc = app.api.read_sensor("accelerometer")
ok = app.api.check_permission("CAMERA") # {permission, granted}

Only an allow-listed set of methods is reachable from JS (enpaf.api(...)); see Device Capabilities.


Built-in bridge handlers

These are registered automatically and power the JS helpers:

Name Used by
__enpaf_ping enpaf.device.getInfo()
__enpaf_storage_get / _set / _delete enpaf.storage.*
__enpaf_get_config reading enpaf.json from JS
__enpaf_api enpaf.api(method, args) / enpaf.sensors.* / enpaf.permissions.*
__enpaf_mod enpaf.mod(module, method, args) / enpaf.wifi.* / enpaf.bluetooth.* / …

You can call any of these from JS too, but prefer the typed helpers in JavaScript Bridge.

Clone this wiki locally