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
Bluetooth functionality requires specific hardware support. Not all Shelly devices have Bluetooth capabilities. Check your device specifications.
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_msthat satisfies all requests activescan if any request requires it- Shortest
interval_msamong all requests - Longest
window_msamong all requests (max 100ms) - Lowest
rssi_thramong 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= 0BLE.Scanner.SCAN_STOP= 1BLE.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
| Property | Type | Description | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| function or null | Function for scan events (null to unsubscribe)
| ||||||||||||||||||||||||||||||||||||||||||||||||
| 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
| Property | Type | Description |
|---|---|---|
| object | Scan configuration object |
| function | Optional event callback function |
| any | Data passed to callback |
Returns: Scan options object on success, or null if scan failed to start
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);
}
});
| Property | Type | Description | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| object | Scan configuration
| |||||||||||||||||||||
| function | Optional event callback | |||||||||||||||||||||
| 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= 0x1BLE.GAP.ADDRESS_TYPE_RANDOM_STATIC= 0x2BLE.GAP.ADDRESS_TYPE_RANDOM_NON_RESOLVABLE= 0x3BLE.GAP.ADDRESS_TYPE_RANDOM_RESOLVABLE= 0x4
EIR Types:
BLE.GAP.EIR_FLAGS= 0x1BLE.GAP.EIR_SERVICE_16= 0x3BLE.GAP.EIR_SERVICE_128= 0x7BLE.GAP.EIR_SHORT_NAME= 0x8BLE.GAP.EIR_FULL_NAME= 0x9BLE.GAP.EIR_TX_POWER_LEVEL= 0xABLE.GAP.EIR_SERVICE_DATA_16= 0x16BLE.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
- 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