Skip to main content
Version: 1.0

Bluetooth

Since version 0.12.0

The Bluetooth Low Energy (BLE) API enables scripts to scan for and interact with Bluetooth devices, including beacons, sensors, and other BLE peripherals.

Overview

The BLE global object provides:

  • Scanner - Discover and monitor Bluetooth devices
  • GAP - Parse advertisement data using Generic Access Profile utilities
Device Support

Bluetooth functionality requires specific hardware support. Not all Shelly devices have Bluetooth capabilities. Check your device specifications.

Scan Manager Behavior

Since firmware 1.5.0, the Enhanced Scan Manager handles all Bluetooth scanning. Multiple scripts can request scans simultaneously, and the manager will merge scan parameters using the most aggressive settings:

  • Shortest duration_ms that satisfies all requests
  • active scan if any request requires it
  • Shortest interval_ms among all requests
  • Longest window_ms among all requests (max 100ms)
  • Lowest rssi_thr among all requests
  • Each script's filters are applied independently

This means your actual scan parameters may differ from what you requested if other scripts or system components are also scanning.

BLE.Scanner

The Scanner object manages Bluetooth device discovery.

Constants

Scan Events:

  • BLE.Scanner.SCAN_START = 0
  • BLE.Scanner.SCAN_STOP = 1
  • BLE.Scanner.SCAN_RESULT = 2

Duration:

  • BLE.Scanner.INFINITE_SCAN = -1 (perpetual scanning)

BLE.Scanner.subscribe()

Subscribe to scan events.

Syntax:

BLE.Scanner.subscribe(callback[, userdata]) -> undefined
PropertyTypeDescription

callback

function or null

Function for scan events (null to unsubscribe)

PropertyTypeDescription

event

number

Event type (SCAN_START, SCAN_STOP, or SCAN_RESULT)

result

object

Scan result for SCAN_RESULT events

PropertyTypeDescription

addr

string

Device MAC address

addr_type

number

Address type

advData

string

Advertisement data

scanRsp

string

Scan response (active scans)

rssi

number

Signal strength

flags

number

Advertisement flags

local_name

string

Device name

manufacturer_data

object

Manufacturer-specific data

service_uuids

array

Advertised service UUIDs

service_data

object

Service-specific data

tx_power_level

number

Transmit power

userdata

any

Passed userdata

userdata

any

Data to pass to callback

Example:

BLE.Scanner.subscribe(function(event, result) {
switch(event) {
case BLE.Scanner.SCAN_START:
console.log("Scan started");
break;
case BLE.Scanner.SCAN_STOP:
console.log("Scan stopped");
break;
case BLE.Scanner.SCAN_RESULT:
console.log("Found device:", result.addr, "RSSI:", result.rssi);
if (result.local_name) {
console.log("Name:", result.local_name);
}
break;
}
});

BLE.Scanner.start()

Start scanning for Bluetooth devices with optional filters and configuration.

Syntax:

BLE.Scanner.start(options[, callback[, userdata]]) -> object or null
PropertyTypeDescription

options

object

Scan configuration object

callback

function

Optional event callback function

userdata

any

Data passed to callback

Returns: Scan options object on success, or null if scan failed to start

Scan Options

For complete documentation of scan options, see:

  • Scan Options - Configure duration, interval, window, RSSI threshold

Basic Example

// Start 30-second scan with RSSI filtering
BLE.Scanner.start({
duration_ms: 30000,
active: true,
interval_ms: 100,
window_ms: 50,
rssi_thr: -70 // Only devices with RSSI > -70 dBm
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
console.log("Device:", result.addr, "RSSI:", result.rssi);
}
});
PropertyTypeDescription

options

object

Scan configuration

PropertyTypeDescription

duration_ms

number

Scan duration (use INFINITE_SCAN for continuous)

active

boolean

Active scan (true) or passive (false)

interval_ms

number

Scan interval in ms

window_ms

number

Scan window in ms (max 100ms)

rssi_thr

number

RSSI threshold filter

filters

array

Device filters

callback

function

Optional event callback

userdata

any

Data for callback

Returns: Scan options object or null if failed

Example:

// Start 30-second scan
BLE.Scanner.start({
duration_ms: 30000,
active: true,
interval_ms: 100,
window_ms: 50
}, function(event, result) {
if (event === BLE.Scanner.SCAN_RESULT) {
console.log("Device:", result.addr);
}
});

// Continuous scan with RSSI filter
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
rssi_thr: -70 // Only devices with RSSI > -70 dBm
});

BLE.Scanner.stop()

Stop the current scan.

Returns: true if stopped successfully, false on error

BLE.Scanner.isRunning()

Check if a scan is active.

Returns: true if scanning, false otherwise

BLE.Scanner.getScanOptions()

Get current scan configuration.

Returns: Options object or null if not scanning

BLE.GAP

Generic Access Profile utilities for parsing advertisement data.

Constants

Address Types:

  • BLE.GAP.ADDRESS_TYPE_PUBLIC = 0x1
  • BLE.GAP.ADDRESS_TYPE_RANDOM_STATIC = 0x2
  • BLE.GAP.ADDRESS_TYPE_RANDOM_NON_RESOLVABLE = 0x3
  • BLE.GAP.ADDRESS_TYPE_RANDOM_RESOLVABLE = 0x4

EIR Types:

  • BLE.GAP.EIR_FLAGS = 0x1
  • BLE.GAP.EIR_SERVICE_16 = 0x3
  • BLE.GAP.EIR_SERVICE_128 = 0x7
  • BLE.GAP.EIR_SHORT_NAME = 0x8
  • BLE.GAP.EIR_FULL_NAME = 0x9
  • BLE.GAP.EIR_TX_POWER_LEVEL = 0xA
  • BLE.GAP.EIR_SERVICE_DATA_16 = 0x16
  • BLE.GAP.EIR_MANUFACTURER_SPECIFIC_DATA = 0xFF

Parsing Functions

BLE.GAP.parseName(data)

Extract device name from advertisement data.

BLE.GAP.parseManufacturerData(data)

Extract manufacturer-specific data.

BLE.GAP.parseServiceData(data, uuid)

Extract data for a specific service UUID.

BLE.GAP.hasService(data, uuid)

Check if a service UUID is advertised.

Complete Examples

Temperature Sensor Monitor

// Monitor BLE temperature sensors
BLE.Scanner.start({
duration_ms: BLE.Scanner.INFINITE_SCAN,
active: false
}, function(event, result) {
if (event !== BLE.Scanner.SCAN_RESULT) return;

// Check for temperature service
if (BLE.GAP.hasService(result.advData, "181a")) {
let serviceData = BLE.GAP.parseServiceData(result.advData, "181a");
if (serviceData.length >= 2) {
// Parse temperature (example format)
let temp = (serviceData.charCodeAt(0) | (serviceData.charCodeAt(1) << 8)) / 100;
console.log("Temperature from", result.addr, ":", temp, "°C");
}
}
});

iBeacon Scanner

// Scan for iBeacons
BLE.Scanner.subscribe(function(event, result) {
if (event !== BLE.Scanner.SCAN_RESULT) return;

let mfgData = BLE.GAP.parseManufacturerData(result.advData);

// Check for Apple iBeacon (0x004C)
if (mfgData.length >= 23 &&
mfgData.charCodeAt(0) === 0x4C &&
mfgData.charCodeAt(1) === 0x00) {

// Extract UUID (bytes 4-19)
let uuid = "";
for (let i = 4; i < 20; i++) {
uuid += ("0" + mfgData.charCodeAt(i).toString(16)).slice(-2);
if (i === 7 || i === 9 || i === 11 || i === 13) uuid += "-";
}

// Extract major/minor
let major = (mfgData.charCodeAt(20) << 8) | mfgData.charCodeAt(21);
let minor = (mfgData.charCodeAt(22) << 8) | mfgData.charCodeAt(23);

console.log("iBeacon:", uuid, "Major:", major, "Minor:", minor, "RSSI:", result.rssi);
}
});

BLE.Scanner.start({duration_ms: BLE.Scanner.INFINITE_SCAN});

Performance Considerations

Resource Usage
  • BLE scanning is CPU and memory intensive
  • Filter results to specific devices when possible
  • Use passive scanning when active scanning isn't needed
  • Consider using intervals for periodic scanning instead of continuous
  • Results are deduplicated for 3 seconds to reduce load

Version Notes

  • v0.12.0: Initial BLE support
  • v1.0.0: 3-second deduplication added
  • v1.5.0: Multiple script scan support
  • v1.6.0: Bluetooth start/stop without reboot
  • v1.7.0: CPU throttling at 25% usage