Skip to content

Device Capabilities

aaalllexxx edited this page Jun 29, 2026 · 1 revision

Device Capabilities

ENPAF exposes device hardware through capability modules. Each module is:

  • a Python object on the app (app.wifi, app.bluetooth, …), and
  • reachable from JS via enpaf.<module>.* (typed shortcuts) or enpaf.mod(module, method, args).

You can drive every capability from Python or from JS — they call the same code. Off-device (dev), methods return informative stub values (dev: true) so your UI still works in the browser.

Module Python JS Highlights
Device app.device enpaf.device toast, vibrate, share, clipboard, notify, open URL, orientation, info
Sensors app.sensors enpaf.sensors accelerometer, gyroscope, light, … snapshots
Location app.location enpaf.location last-known location
Wi-Fi app.wifi enpaf.wifi scan, connect, events
Bluetooth app.bluetooth enpaf.bluetooth discover, connect, send/receive
NFC app.nfc enpaf.nfc read/write every record type, lock
Audio app.audio enpaf.audio mic level
Battery app.battery enpaf.battery level, charging, network
Notifications app.notifications enpaf.notifications system notifications
Media app.media enpaf.media camera, video, pick, record
Permissions app.permissions enpaf.permissions on-demand runtime requests
Biometric app.biometric enpaf.biometric fingerprint / face / device credential

Add the matching entries to enpaf.json permissions (and sometimes features) for anything that touches hardware. See enpaf.json Configuration.


Permissions (request on demand)

Dangerous permissions must be requested at runtime — and ENPAF lets you do it when you choose, not at launch.

const res = await enpaf.permissions.request(["CAMERA", "RECORD_AUDIO"]);
// res = { granted: [...], denied: [...], results: {...} }
# Python: check current state
app.api.check_permission("CAMERA")              # {permission, granted}
app.api.check_permissions(["CAMERA", "NFC"])    # {granted, denied, results}

Results also arrive as the permission_result event:

@app.on("permission_result")
def on_perm(data):
    print("granted", data["granted"], "denied", data["denied"])

Sensors

const a = await enpaf.sensors.read("accelerometer"); // {values:[x,y,z], ...}
const all = await enpaf.sensors.snapshot();          // common sensors + state
const list = await enpaf.sensors.list();             // hardware present
acc = app.api.read_sensor("accelerometer")
report = app.api.get_sensor_snapshot()

Supported names: accelerometer, gyroscope, magnetometer, light, proximity, pressure, gravity, linear_acceleration, rotation_vector, step_counter, step_detector, humidity, temperature, heart_rate.


Location

const loc = await enpaf.location.get();   // {latitude, longitude, accuracy, ...}
loc = app.location.get()                  # or app.api.get_location()

Needs FINE_LOCATION (and a runtime request). Returns the last-known fix.


Wi-Fi

Scanning is asynchronous and streams results as events.

enpaf.wifi.onResult((n) => addRow(n.ssid, n.rssi));
enpaf.wifi.onFinished(() => done());
enpaf.wifi.scan();
await enpaf.wifi.connect("MySSID", "secret");
# Python: blocking helper that collects the scan synchronously
nets = app.wifi.scan_sync(timeout=10)["networks"]
app.wifi.connect(ssid="MySSID", password="secret")

Methods: status, info, scan, networks, enable, connect, disconnect. Events: wifi_scan_result, wifi_scan_finished, wifi_connected, wifi_error.


Bluetooth (Classic)

One connection at a time; discovery streams devices as events.

enpaf.bluetooth.onFound((d) => addDevice(d.name, d.address));
enpaf.bluetooth.discover();
await enpaf.bluetooth.connect("AA:BB:CC:DD:EE:FF");
enpaf.bluetooth.onData((msg) => console.log("rx", msg));
enpaf.bluetooth.send("hello");
devs = app.bluetooth.discover_sync(timeout=12)["devices"]
app.bluetooth.send(text="hello")

Methods: status, enable, paired, discover, stop_discovery, connect, listen, send, disconnect. Events: bluetooth_device_found, bluetooth_connected, bluetooth_data, bluetooth_disconnected, bluetooth_discovery_finished, bluetooth_error.


NFC

Read and write every NDEF record type, and optionally lock a tag.

const data = await enpaf.nfc.read();              // {records:[...]}
await enpaf.nfc.armUri("https://example.com");    // then tap a tag to write
enpaf.nfc.onTag((t) => console.log("tag", t.id));

Writing: prefer the arm-then-tap helpers (armText, armUri, armApp, armMime, armWifi, armContact, armRecords, armLock) — they resolve when the user taps a tag. The immediate write* variants act on the last-tapped tag.

@app.on("nfc_tag")
def on_tag(data):
    print("tag", data["id"], "from_launch", data["from_launch"])

Locking a tag (enpaf.nfc.lock() / armLock()) is permanent and irreversible.


Media

const photo = await enpaf.media.takePicture();
const video = await enpaf.media.recordVideo();
const picked = await enpaf.media.pickMedia("image");   // or "video"
const audio = await enpaf.media.recordAudio(5);        // seconds

Needs CAMERA / RECORD_AUDIO (and runtime requests).


Battery, Audio, Notifications, Device

const b = await enpaf.battery.info();        // {level, charging}
const net = await enpaf.battery.network();   // {connected, type}
const lvl = await enpaf.audio.level(1);      // mic peak amplitude
enpaf.notifications.notify({ title: "Hi", text: "Done" });

enpaf.device.toast("Saved");
enpaf.device.vibrate(200);
enpaf.device.clipboard("copied text");
enpaf.device.openUrl("https://example.com");
enpaf.device.setOrientation("landscape");

Biometric

const { available } = await enpaf.biometric.available();
if (available) {
  const res = await enpaf.biometric.authenticate({
    title: "Unlock", subtitle: "Use your fingerprint",
  });
  if (res.success) unlock();
}

Requires USE_BIOMETRIC. Resolves with { success, error }; also fires the biometric_result event.

Implementation note: Chaquopy can't subclass abstract Java classes, so the biometric prompt and broadcast receivers are implemented in MainActivity (Java) and forwarded to Python. See Architecture.


Calling capabilities the generic way

Anything without a typed shortcut is reachable via the gateways:

enpaf.mod("wifi", "status");                 // -> app.wifi.status()
enpaf.api("get_battery", {});                // -> app.api.get_battery()

Both validate the method against an allow-list and reject anything else.

Clone this wiki locally