Crea APK gratis

API del bridge JavaScript

Web Bluetooth

La forma di navigator.bluetooth, gestita in modo nativo così una pagina scritta per Chrome funziona senza modifiche.

navigator.bluetooth standard web#

navigator.bluetooth — Web Bluetooth

Esempio

Web Bluetooth standard: lo stesso codice navigator.bluetooth che scriveresti per Chrome funziona nell'app installata, per qualsiasi dispositivo Bluetooth Low Energy.

Restituisce: Promise, come nello standard web. I valori sono DataView. Richiede: Bluetooth. Non c'Ăš un interruttore: quando crei da uno ZIP o da una pagina HTML, la build lo attiva da sola se il tuo codice nomina navigator.bluetooth (o BluetoothUUID). Una build da sito web (URL) non puĂČ attivarlo. Senza di esso, navigator.bluetooth non esiste. Il primo requestDevice() chiede l'autorizzazione Dispositivi nelle vicinanze (Posizione su Android 11 e precedenti). Se il Bluetooth Ăš spento, l'app chiede all'utente di attivarlo e poi apre da sola l'elenco dei dispositivi; su Android 6-11 fa lo stesso per l'impostazione Posizione, perchĂ© quelle versioni non trovano nessun dispositivo Bluetooth finchĂ© la Posizione Ăš spenta.

Fascia cardio, da un tocco su un pulsante. navigator.bluetooth esiste prima dell'esecuzione del tuo primo script, quindi il rilevamento della funzione all'inizio della pagina funziona; controllare dentro il gestore, come qui, resta comunque l'abitudine piĂč sicura:

var hrDevice = null;

document.getElementById('connect').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    hrDevice = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['heart_rate'] }],        // or { name: 'Polar H10' } / { namePrefix: 'Polar' }
      optionalServices: ['battery_service']
    });
    hrDevice.addEventListener('gattserverdisconnected', function () {
      showStatus('Strap disconnected');
    });
    var server = await hrDevice.gatt.connect();
    var hr = await server.getPrimaryService('heart_rate');
    var chr = await hr.getCharacteristic('heart_rate_measurement');
    chr.addEventListener('characteristicvaluechanged', function (e) {
      var v = e.target.value;                                   // DataView
      var bpm = (v.getUint8(0) & 1) ? v.getUint16(1, true) : v.getUint8(1);
      document.getElementById('bpm').textContent = bpm;
    });
    await chr.startNotifications();

    var battery = await server.getPrimaryService('battery_service');
    var level = await (await battery.getCharacteristic('battery_level')).readValue();
    document.getElementById('battery').textContent = level.getUint8(0) + '%';
  } catch (err) {
    // NotFoundError: user cancelled the chooser, Bluetooth is off, or no such service.
    // SecurityError: permission denied.  NetworkError: connection lost.
    showStatus(err.name + ': ' + err.message);
  }
});

document.getElementById('disconnect').addEventListener('click', function () {
  if (hrDevice && hrDevice.gatt.connected) hrDevice.gatt.disconnect();
});

Scrittura. writeValue e writeValueWithResponse attendono la conferma del dispositivo; writeValueWithoutResponse no:

async function sendCommands(server) {
  // Heart Rate Control Point: 0x01 = reset energy expended
  var hr = await server.getPrimaryService('heart_rate');
  var ctrl = await hr.getCharacteristic('heart_rate_control_point');
  await ctrl.writeValueWithResponse(new Uint8Array([0x01]));

  // A custom UART-style service (full 128-bit UUIDs work everywhere)
  var uart = await server.getPrimaryService('6e400001-b5a3-f393-e0a9-e50e24dcca9e');
  var rx = await uart.getCharacteristic('6e400002-b5a3-f393-e0a9-e50e24dcca9e');
  await rx.writeValueWithoutResponse(new TextEncoder().encode('LED ON\n'));
}

Esploratore di dispositivi. Scegli un dispositivo vicino qualsiasi ed elenca ogni suo servizio e caratteristica, con ciò che ciascuno può fare:

document.getElementById('explore').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    var device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true });
    var server = await device.gatt.connect();
    var lines = [];
    for (var service of await server.getPrimaryServices()) {
      lines.push(service.uuid === BluetoothUUID.getService('battery_service') ? 'Battery' : service.uuid);
      var chars = await service.getCharacteristics().catch(function () { return []; });
      chars.forEach(function (c) {
        var can = Object.keys(c.properties).filter(function (k) { return c.properties[k]; });
        lines.push('  ' + c.uuid + '  ' + can.join(', '));
      });
    }
    document.getElementById('tree').textContent = lines.join('\n');
    device.gatt.disconnect();
  } catch (err) {
    showStatus(err.name + ': ' + err.message);
  }
});

Sensore di velocità e cadenza della bicicletta. Un sensore (o una coppia separata di velocità e cadenza) sul servizio standard CSC. I valori sono totali progressivi, quindi velocità e cadenza si ricavano dalla differenza tra due notifiche:

var WHEEL_MM = 2105;                       // 700x25c; set your own wheel circumference
var last = null;

async function connectCsc() {
  var device = await navigator.bluetooth.requestDevice({
    filters: [{ services: ['cycling_speed_and_cadence'] }]      // same as { services: [0x1816] }
  });
  device.addEventListener('gattserverdisconnected', function () { showStatus('Sensor disconnected'); });
  var server = await device.gatt.connect();
  var csc = await server.getPrimaryService('cycling_speed_and_cadence');
  var chr = await csc.getCharacteristic('csc_measurement');
  chr.addEventListener('characteristicvaluechanged', function (e) {
    var v = e.target.value, flags = v.getUint8(0), i = 1, now = {};
    if (flags & 1) { now.wheels = v.getUint32(i, true); now.wheelTime = v.getUint16(i + 4, true); i += 6; }
    if (flags & 2) { now.cranks = v.getUint16(i, true); now.crankTime = v.getUint16(i + 2, true); }
    if (last) {
      if (now.wheels !== undefined && now.wheelTime !== last.wheelTime) {
        var dt = ((now.wheelTime - last.wheelTime) & 0xffff) / 1024;          // seconds
        var kmh = (now.wheels - last.wheels) * WHEEL_MM / 1e6 / (dt / 3600);
        document.getElementById('speed').textContent = kmh.toFixed(1) + ' km/h';
      }
      if (now.cranks !== undefined && now.crankTime !== last.crankTime) {
        var ct = ((now.crankTime - last.crankTime) & 0xffff) / 1024;
        var rpm = ((now.cranks - last.cranks) & 0xffff) / ct * 60;
        document.getElementById('cadence').textContent = Math.round(rpm) + ' rpm';
      }
    }
    last = Object.assign({}, last, now);
  });
  await chr.startNotifications();
}

Mostrare ogni errore sullo schermo. Ogni passaggio viene rifiutato con un errore con nome, così la pagina può dire all'utente cosa fare invece di restare in silenzio:

async function connectWithMessage(connect) {
  try {
    await connect();
    showStatus('Connected');
  } catch (err) {
    var msg = {
      NotFoundError: 'No sensor chosen, Bluetooth was left off, or the sensor lacks that service.',
      SecurityError: 'Bluetooth permission was refused. Allow "Nearby devices" in the app settings.',
      NetworkError: 'The sensor went out of range or is connected to another app or watch. Try again.',
      TypeError: 'Programming error: ' + err.message
    }[err.name] || err.message;
    showStatus(err.name + ': ' + msg);
  }
}
document.getElementById('scan-hr').addEventListener('click', function () { connectWithMessage(connectHeartRate); });
document.getElementById('scan-csc').addEventListener('click', function () { connectWithMessage(connectCsc); });

Note: Il selettore dei dispositivi è disegnato dall'app e cerca per 30 secondi, poi propone «Cerca di nuovo»; una pagina può raggiungere solo un dispositivo scelto dall'utente. getAvailability() risponde se il telefono ha il Bluetooth in generale (come in Chrome), non se è acceso: requestDevice() chiede di accenderlo. Un sensore che invia il proprio nome solo nella risposta di scansione viene comunque elencato per nome e corrisponde a namePrefix. Una prima connessione che Android interrompe subito viene ritentata prima che connect() venga rifiutato. Un nome di servizio scritto male in filters o optionalServices viene rifiutato con TypeError. Un filtro services corrisponde solo ai servizi che il dispositivo annuncia. Dopo connect() si può usare ogni servizio del dispositivo, anche quelli non presenti in optionalServices (inseriscili comunque, Chrome ne ha bisogno). Disponibili anche: getAvailability(), getPrimaryServices(), getCharacteristics(), stopNotifications(), characteristic.properties, device.forget(), BluetoothUUID.getService/getCharacteristic/canonicalUUID. getDevices(), requestLEScan(), watchAdvertisements() e i descrittori vengono rifiutati con NotSupportedError. Per i sensori fitness standard con i numeri già interpretati, vedi bleStartScan.

wbConnect bridge#

window.WebToApk.wbConnect(requestId: String, deviceId: String)

Trasporto grezzo per WebBluetoothPolyfill. Una pagina non lo chiama mai direttamente: chiama navigator.bluetooth, esattamente come farebbe in un browser.

Descritta dal suo gruppo, Web Bluetooth (navigator.bluetooth), e non singolarmente.

Esempio

Trasporto interno dietro device.gatt.connect(). Le pagine usano navigator.bluetooth; non chiamare questo direttamente.

Restituisce: (tramite gatt.connect()) una Promise del BluetoothRemoteGATTServer, dopo che i servizi del dispositivo sono stati individuati. Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice nomina navigator.bluetooth).

async function connectAndList(device) {
  try {
    var server = await device.gatt.connect();
    var services = await server.getPrimaryServices();
    services.forEach(function (s) { console.log('service', s.uuid); });
    return server;
  } catch (err) {
    showStatus('Could not connect: ' + err.message);    // NetworkError, NotFoundError
    return null;
  }
}

Riconnettiti quando il dispositivo cade. Chiamare di nuovo connect() sullo stesso dispositivo funziona:

device.addEventListener('gattserverdisconnected', async function () {
  showStatus('Reconnecting...');
  try { await connectAndList(device); showStatus('Connected'); } catch (e) {}
});

Collegati a due sensori contemporaneamente, per esempio una fascia cardio e un misuratore di potenza:

async function connectBoth(hrDevice, powerDevice) {
  var servers = await Promise.all([hrDevice.gatt.connect(), powerDevice.gatt.connect()]);
  var hr = await servers[0].getPrimaryService('heart_rate');
  var power = await servers[1].getPrimaryService('cycling_power');
  return {
    heartRate: await hr.getCharacteristic('heart_rate_measurement'),
    power: await power.getCharacteristic('cycling_power_measurement')
  };
}

Note: Firma diretta wbConnect(requestId, deviceId). Connettersi a un dispositivo giĂ connesso si risolve di nuovo subito. getPrimaryService(s) viene rifiutato con NetworkError prima che connect() sia terminato. PiĂč dispositivi possono essere connessi contemporaneamente.

wbDisconnect bridge#

window.WebToApk.wbDisconnect(deviceId: String)

Trasporto grezzo per WebBluetoothPolyfill. Una pagina non lo chiama mai direttamente: chiama navigator.bluetooth, esattamente come farebbe in un browser.

Descritta dal suo gruppo, Web Bluetooth (navigator.bluetooth), e non singolarmente.

Esempio

Trasporto interno dietro device.gatt.disconnect() (e device.forget()). Le pagine usano navigator.bluetooth; non chiamarlo direttamente.

Restituisce: niente (disconnect() è sincrono, come nello standard web). Il dispositivo lancia poi gattserverdisconnected. Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice menziona navigator.bluetooth).

var device = null;   // from navigator.bluetooth.requestDevice(...)

document.getElementById('stop').addEventListener('click', function () {
  if (device && device.gatt.connected) device.gatt.disconnect();
});

// Fires for your own disconnect() and when the device goes away by itself.
function watch(dev) {
  dev.addEventListener('gattserverdisconnected', function () {
    showStatus('Disconnected from ' + (dev.name || dev.id));
  });
}

Distingui il tuo Stop dal sensore che esce dal raggio d'azione e riconnettiti solo nel secondo caso:

var userStopped = false;

function stopSession(device) {
  userStopped = true;
  if (device.gatt.connected) device.gatt.disconnect();
}

function watchDrops(device) {
  device.addEventListener('gattserverdisconnected', function () {
    if (userStopped) { userStopped = false; showStatus('Session ended'); return; }
    showStatus('Sensor out of range, reconnecting...');
    device.gatt.connect()
      .then(resubscribe)                 // your code: get the characteristics, startNotifications()
      .catch(function () { showStatus('Could not reconnect'); });
  });
}

Un pulsante "Rimuovi sensore": forget() disconnette ed elimina il dispositivo, e non segue alcun gattserverdisconnected:

var device = null;   // from navigator.bluetooth.requestDevice(...)

document.getElementById('forget').addEventListener('click', async function () {
  if (!device) return;
  await device.forget();
  device = null;
  showStatus('Sensor removed. Tap Connect to pick one again.');
});

Note: Firma diretta wbDisconnect(deviceId). Dopo una disconnessione, i vecchi oggetti servizio e caratteristica smettono di ricevere notifiche; dopo il successivo connect(), ottienili di nuovo e chiama di nuovo startNotifications(). L'app chiude ogni connessione quando si chiude.

wbGetCharacteristics bridge#

window.WebToApk.wbGetCharacteristics(requestId: String, deviceId: String, service: String)

Trasporto grezzo per WebBluetoothPolyfill. Una pagina non lo chiama mai direttamente: chiama navigator.bluetooth, esattamente come farebbe in un browser.

Descritta dal suo gruppo, Web Bluetooth (navigator.bluetooth), e non singolarmente.

Esempio

Trasporto interno dietro service.getCharacteristic(uuid) e service.getCharacteristics(). Le pagine usano navigator.bluetooth; non chiamare questo direttamente.

Restituisce: (tramite il servizio) una Promise di una BluetoothRemoteGATTCharacteristic, o di un array di esse, ciascuna con uuid e properties (read, write, writeWithoutResponse, notify, indicate, broadcast, authenticatedSignedWrites). Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice nomina navigator.bluetooth).

async function describe(server) {
  var service = await server.getPrimaryService('heart_rate');

  // One characteristic, by standard name, 16-bit number or full UUID:
  var measurement = await service.getCharacteristic('heart_rate_measurement'); // same as 0x2a37

  // All of them:
  var list = await service.getCharacteristics();
  list.forEach(function (c) {
    var p = c.properties;
    console.log(c.uuid, p.read ? 'read' : '', p.write ? 'write' : '', p.notify ? 'notify' : '');
  });
  return measurement;
}

Trova le caratteristiche di un servizio personalizzato in base a ciò che sanno fare, invece di scrivere i loro UUID direttamente nel codice:

async function findSerialPipes(server) {
  var svc = await server.getPrimaryService('0000ffe0-0000-1000-8000-00805f9b34fb');
  var list = await svc.getCharacteristics();
  var incoming = list.find(function (c) { return c.properties.notify || c.properties.indicate; });
  var outgoing = list.find(function (c) { return c.properties.write || c.properties.writeWithoutResponse; });
  if (!incoming || !outgoing) throw new Error('This device does not look like a serial adapter');
  return { incoming: incoming, outgoing: outgoing };
}

Leggi una caratteristica facoltativa e tratta NotFoundError come «questo dispositivo non ce l'ha»:

var LOCATIONS = ['Other', 'Chest', 'Wrist', 'Finger', 'Hand', 'Ear lobe', 'Foot'];

async function sensorLocation(server) {
  var hr = await server.getPrimaryService('heart_rate');
  try {
    var chr = await hr.getCharacteristic('body_sensor_location');   // 0x2a38, optional in the spec
    var v = await chr.readValue();
    return LOCATIONS[v.getUint8(0)] || 'Unknown';
  } catch (err) {
    if (err.name === 'NotFoundError') return null;                   // this strap does not report it
    throw err;
  }
}

Note: firma grezza wbGetCharacteristics(requestId, deviceId, service). Un UUID che il servizio non ha viene rifiutato con NotFoundError; un nome sconosciuto genera TypeError (passa un UUID completo per le caratteristiche personalizzate). L'elenco viene recuperato una sola volta per servizio e poi riutilizzato.

wbIsAvailable bridge#

window.WebToApk.wbIsAvailable(): Boolean

getAvailability(): se il telefono HA il Bluetooth (la risposta di Chrome), non se Ăš acceso. requestDevice() chiede all'utente di accenderlo; rispondere false quando era solo spento faceva sĂŹ che le pagine che controllano prima ("if (!await getAvailability())") dicessero "Bluetooth non supportato" senza mai arrivare a quella richiesta (rapporto fitness 2026-10-03).

Esempio

Trasporto interno dietro navigator.bluetooth.getAvailability(). Le pagine usano navigator.bluetooth; non chiamare questo direttamente.

Restituisce: (tramite getAvailability()) una Promise di true quando la build ha il Bluetooth e il Bluetooth del telefono Ăš attivo, altrimenti false. Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice nomina navigator.bluetooth).

async function updateBluetoothHint() {
  var hint = document.getElementById('bt-hint');
  if (!navigator.bluetooth) { hint.textContent = 'Bluetooth is not available in this app.'; return; }
  var on = await navigator.bluetooth.getAvailability();
  hint.textContent = on ? '' : 'Turn on Bluetooth to connect your sensor.';
}
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) updateBluetoothHint();
});

Abilita il pulsante Connetti solo mentre il Bluetooth Ăš attivo, ricontrollando ogni volta che l'utente torna all'app:

async function refreshConnectButton() {
  var btn = document.getElementById('connect');
  if (!navigator.bluetooth) { btn.hidden = true; return; }
  btn.disabled = !(await navigator.bluetooth.getAvailability());
}
window.addEventListener('load', refreshConnectButton);
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) refreshConnectButton();
});

Mostra un chiaro messaggio "Il Bluetooth Ăš spento" prima di aprire il selettore, invece di un NotFoundError dopo:

document.getElementById('scan').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  if (!(await navigator.bluetooth.getAvailability())) {
    showStatus('Bluetooth is off. Turn it on, then tap Scan again.');
    return;
  }
  try {
    var device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true });
    showStatus('Picked ' + (device.name || device.id));
  } catch (err) {
    showStatus(err.name === 'NotFoundError' ? 'No device chosen.' : err.message);
  }
});

Note: Firma diretta wbIsAvailable(): Boolean (sincrona). Controlla solo l'adattatore: non chiede alcuna autorizzazione. L'evento availabilitychanged dello standard web non viene lanciato, quindi ricontrolla quando la pagina torna visibile.

wbNotify bridge#

window.WebToApk.wbNotify(

Trasporto grezzo per WebBluetoothPolyfill. Una pagina non lo chiama mai direttamente: chiama navigator.bluetooth, esattamente come farebbe in un browser.

Descritta dal suo gruppo, Web Bluetooth (navigator.bluetooth), e non singolarmente.

Esempio

Trasporto interno dietro characteristic.startNotifications() e stopNotifications(). Le pagine usano navigator.bluetooth; non chiamare questo direttamente.

Restituisce: (tramite la caratteristica) una Promise della caratteristica stessa. Ogni nuovo valore fa scattare characteristicvaluechanged su di essa, con il valore in event.target.value (un DataView). Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice nomina navigator.bluetooth).

var cadenceChr = null;

async function startCadence(server) {
  var svc = await server.getPrimaryService('running_speed_and_cadence');
  cadenceChr = await svc.getCharacteristic('rsc_measurement');
  cadenceChr.addEventListener('characteristicvaluechanged', onRsc);
  await cadenceChr.startNotifications();
}

function onRsc(e) {
  var v = e.target.value;
  var kmh = v.getUint16(1, true) / 256 * 3.6;
  var spm = v.getUint8(3);
  show('run', kmh.toFixed(1) + ' km/h, ' + spm + ' steps/min');
}

async function stopCadence() {
  if (!cadenceChr) return;
  cadenceChr.removeEventListener('characteristicvaluechanged', onRsc);
  try { await cadenceChr.stopNotifications(); } catch (e) {}
}

Un termometro la cui misurazione supporta solo le indicazioni si sottoscrive allo stesso modo, qui tramite la proprietà oncharacteristicvaluechanged:

async function watchThermometer(server) {
  var svc = await server.getPrimaryService('health_thermometer');
  var chr = await svc.getCharacteristic('temperature_measurement');
  chr.oncharacteristicvaluechanged = function (e) {
    var v = e.target.value;
    var mantissa = v.getUint32(1, true) & 0x00ffffff;          // IEEE-11073 FLOAT
    if (mantissa & 0x800000) mantissa -= 0x1000000;
    var fahrenheit = v.getUint8(0) & 1;
    show('temp', (mantissa * Math.pow(10, v.getInt8(4))).toFixed(1) + (fahrenheit ? ' °F' : ' °C'));
  };
  await chr.startNotifications();
}

Ricevi righe di testo da un dispositivo di tipo seriale, unendo le notifiche che spezzano una riga a metĂ :

async function listenForLines(server, onLine) {
  var uart = await server.getPrimaryService('6e400001-b5a3-f393-e0a9-e50e24dcca9e');
  var tx = await uart.getCharacteristic('6e400003-b5a3-f393-e0a9-e50e24dcca9e');
  if (!tx.properties.notify && !tx.properties.indicate) throw new Error('This device does not stream data');
  var decoder = new TextDecoder(), buffer = '';
  tx.addEventListener('characteristicvaluechanged', function (e) {
    buffer += decoder.decode(e.target.value, { stream: true });
    var lines = buffer.split('\n');
    buffer = lines.pop();
    lines.forEach(onLine);
  });
  await tx.startNotifications();
  return tx;
}

Note: Firma diretta wbNotify(requestId, deviceId, service, characteristic, enable). Una caratteristica che supporta solo le indicazioni viene sottoscritta automaticamente con le indicazioni. Una caratteristica che non puĂČ notificare viene rifiutata con NotSupportedError. Funziona anche oncharacteristicvaluechanged = fn. Dopo una disconnessione, sottoscrivi di nuovo.

wbRead bridge#

window.WebToApk.wbRead(requestId: String, deviceId: String, service: String, characteristic: String)

Trasporto grezzo per WebBluetoothPolyfill. Una pagina non lo chiama mai direttamente: chiama navigator.bluetooth, esattamente come farebbe in un browser.

Descritta dal suo gruppo, Web Bluetooth (navigator.bluetooth), e non singolarmente.

Esempio

Trasporto interno dietro characteristic.readValue(). Le pagine usano navigator.bluetooth; non chiamare questo direttamente.

Restituisce: (tramite readValue()) una Promise di un DataView; anche characteristic.value viene aggiornato. Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice nomina navigator.bluetooth).

async function readInfo(server) {
  try {
    var battery = await server.getPrimaryService('battery_service');
    var level = await (await battery.getCharacteristic('battery_level')).readValue();
    show('battery', level.getUint8(0) + '%');

    var info = await server.getPrimaryService('device_information');
    var maker = await (await info.getCharacteristic('manufacturer_name_string')).readValue();
    show('maker', new TextDecoder().decode(maker));
  } catch (err) {
    // NotFoundError: the device has no such service/characteristic
    // NotSupportedError: read rejected by the device   NetworkError: not connected / read failed
    show('info', err.message);
  }
}

Interroga periodicamente un sensore che può solo essere letto, qui la caratteristica standard Temperature (0x2a6e, in centesimi di grado):

var pollTimer = null;

async function startTemperaturePolling(server) {
  var env = await server.getPrimaryService('environmental_sensing');
  var temp = await env.getCharacteristic(0x2a6e);
  pollTimer = setInterval(async function () {
    try {
      var v = await temp.readValue();
      show('temp', (v.getInt16(0, true) / 100).toFixed(1) + ' °C');
    } catch (err) {
      clearInterval(pollTimer);          // NetworkError once the sensor is gone
      show('temp', err.message);
    }
  }, 5000);
}

Compila un pannello «Informazioni su questo dispositivo» avviando più letture insieme; vengono eseguite una dopo l'altra al posto tuo:

async function readDeviceInfo(server) {
  var info = await server.getPrimaryService('device_information');
  var names = ['model_number_string', 'serial_number_string', 'firmware_revision_string'];
  var decoder = new TextDecoder();
  var results = await Promise.allSettled(names.map(async function (name) {
    var chr = await info.getCharacteristic(name);
    return decoder.decode(await chr.readValue());
  }));
  results.forEach(function (r, i) {
    show(names[i], r.status === 'fulfilled' ? r.value : '-');   // missing ones reject with NotFoundError
  });
}

Note: firma grezza wbRead(requestId, deviceId, service, characteristic). Le letture, le scritture e le modifiche alle notifiche su un dispositivo vengono eseguite una dopo l'altra nell'ordine in cui le chiami, quindi puoi lanciarne diverse senza aspettare tra l'una e l'altra.

wbRequestDevice bridge#

window.WebToApk.wbRequestDevice(requestId: String, optionsJson: String)

Trasporto grezzo per WebBluetoothPolyfill. Una pagina non lo chiama mai direttamente: chiama navigator.bluetooth, esattamente come farebbe in un browser.

Descritta dal suo gruppo, Web Bluetooth (navigator.bluetooth), e non singolarmente.

Esempio

Trasporto interno dietro navigator.bluetooth.requestDevice(options): mostra il selettore di dispositivi dell'app. Le pagine usano navigator.bluetooth.requestDevice; non chiamare questo direttamente.

Restituisce: (tramite requestDevice) una Promise di un BluetoothDevice {id, name, gatt}. Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice nomina navigator.bluetooth) e l'autorizzazione Dispositivi nelle vicinanze, richiesta al primo utilizzo.

document.getElementById('pick').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    var device = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['cycling_power'] }, { namePrefix: 'KICKR' }],
      optionalServices: ['battery_service']
    });
    showStatus('Picked ' + (device.name || device.id));
  } catch (err) {
    if (err.name === 'NotFoundError') showStatus('No device chosen.');   // also: Bluetooth off
    else showStatus(err.name + ': ' + err.message);                     // SecurityError = permission
  }
});

Per elencare tutti i dispositivi vicini invece di filtrarli: requestDevice({ acceptAllDevices: true }).

Scegli un dispositivo personalizzato in base al nome esatto o al prefisso del nome, elencando il suo servizio a 128 bit così da poterlo usare dopo la connessione:

async function pickLamp() {
  try {
    return await navigator.bluetooth.requestDevice({
      filters: [{ name: 'Desk Lamp' }, { namePrefix: 'LAMP-' }],   // either one matches
      optionalServices: ['0000ffe0-0000-1000-8000-00805f9b34fb']
    });
  } catch (err) {
    if (err.name === 'SecurityError') showStatus('Allow Nearby devices to find your lamp.');
    else if (err.name !== 'NotFoundError') showStatus(err.message);  // NotFoundError = cancelled
    return null;
  }
}

Richiedi il dispositivo una sola volta per sessione e riutilizzalo nei tocchi successivi (getDevices() non è disponibile, quindi conserva l'oggetto che hai ottenuto):

var scale = null;

document.getElementById('weigh').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    if (!scale) {
      scale = await navigator.bluetooth.requestDevice({
        filters: [{ services: ['weight_scale'], namePrefix: 'MI' }]   // both keys must match
      });
    }
    var server = await scale.gatt.connect();
    var svc = await server.getPrimaryService('weight_scale');
    var chr = await svc.getCharacteristic('weight_measurement');
    chr.addEventListener('characteristicvaluechanged', onWeight);
    await chr.startNotifications();
  } catch (err) {
    showStatus(err.name + ': ' + err.message);
  }
});

Note: Firma diretta wbRequestDevice(requestId, optionsJson); la risposta torna al polyfill, non alla pagina. Chiama requestDevice da un tocco. I filtri usano services (deve essere annunciato), name (esatto) e namePrefix; un filtro corrisponde quando tutte le sue chiavi corrispondono, e il dispositivo corrisponde quando corrisponde un qualsiasi filtro. Se non ci sono nĂ© filters nĂ© acceptAllDevices → TypeError. Annullare il selettore viene rifiutato con NotFoundError. Il selettore cerca per 30 secondi. device.id Ăš l'indirizzo Bluetooth del dispositivo.

wbWrite bridge#

window.WebToApk.wbWrite(

Trasporto grezzo per WebBluetoothPolyfill. Una pagina non lo chiama mai direttamente: chiama navigator.bluetooth, esattamente come farebbe in un browser.

Descritta dal suo gruppo, Web Bluetooth (navigator.bluetooth), e non singolarmente.

Esempio

Trasporto interno dietro characteristic.writeValue(), writeValueWithResponse() e writeValueWithoutResponse(). Le pagine usano navigator.bluetooth; non chiamare questo direttamente.

Restituisce: (tramite la caratteristica) una Promise di undefined. Richiede: Bluetooth (attivato da una build ZIP o HTML quando il tuo codice menziona navigator.bluetooth).

Con risposta (writeValue è lo stesso): si risolve quando il dispositivo conferma la scrittura.

async function setTargetPower(server, watts) {
  var ftms = await server.getPrimaryService('fitness_machine');
  var cp = await ftms.getCharacteristic('00002ad9-0000-1000-8000-00805f9b34fb'); // FTMS control point
  await cp.writeValueWithResponse(new Uint8Array([0x00]));                       // request control
  var cmd = new DataView(new ArrayBuffer(3));
  cmd.setUint8(0, 0x05);                 // set target power
  cmd.setInt16(1, watts, true);
  await cp.writeValueWithResponse(cmd);
}

Senza risposta: piĂč veloce, per flussi in cui la perdita di un pacchetto non Ăš importante.

async function sendText(characteristic, text) {
  if (!characteristic.properties.writeWithoutResponse) throw new Error('This characteristic needs writeValue');
  await characteristic.writeValueWithoutResponse(new TextEncoder().encode(text));
}

Invia un lungo comando di testo in pezzi da 20 byte. L'app non chiede al dispositivo una dimensione di pacchetto maggiore, quindi 20 byte Ăš ciĂČ che entra in una scrittura:

async function sendLine(rx, text) {
  var bytes = new TextEncoder().encode(text + '\n');
  for (var i = 0; i < bytes.length; i += 20) {
    await rx.writeValueWithoutResponse(bytes.subarray(i, i + 20));
  }
}
// sendLine(rxCharacteristic, 'SET COLOR 255 120 0 BRIGHTNESS 80');

Note: firma grezza wbWrite(requestId, deviceId, service, characteristic, valueBase64, withResponse). Il valore può essere un ArrayBuffer, qualsiasi array tipizzato, un DataView o un normale array di byte; qualsiasi altra cosa genera TypeError. Errori: NotSupportedError (scrittura rifiutata), NetworkError (scrittura non riuscita o non connesso), NotFoundError.

Generato dal runtime dell'app e dai suoi file di esempio a ogni build della documentazione. Leggilo in Markdown · Tutte le famiglie.

Verificato rispetto al bridge distribuito il 22/10/2026.