The beacons bridge ranges and monitors iBeacons, scans Eddystone frames and broadcasts the device as a beacon, backed by Shiny.Beacons.
dotnet add package Shiny.AppDeviceBridge.Beacons
builder. UseAppDeviceBridge (
bridge => bridge. AddBeaconsBridge (),
Client
C#
TypeScript
IBeaconsBridge
AddBeaconsBridgeClient()
BeaconsBridge
AddBeaconsBridge() registers all four features. To leave some out, pass BeaconFeatures flags: for
example AddBeaconsBridge(BeaconFeatures.Ranging | BeaconFeatures.Eddystone) skips monitoring’s background location and
broadcasting’s advertise permission. A feature left out answers 501. So does one the platform reports as NotSupported,
such as iBeacon ranging and monitoring on macOS, where CoreLocation has neither. GET beacons/status shows each
feature’s access.
Topic
Details
Ranging
POST beacons/ranging takes a region: identifier, uuid, and optionally major, then minor. Each beacon seen arrives as beacons.ranged, tagged with the region, with RSSI, an estimated distance and a proximity bucket. Listen first: without a listener it answers 409. Several regions can range at once, and all of them stop once nothing listens. Ranging is a foreground activity.
Monitoring
POST beacons/regions watches a region for enter and exit, in the background and across restarts. Transitions arrive as beacons.region while a page is open, and at the beacon handler otherwise. Without permission it answers 409.
Eddystone
POST beacons/eddystone sends UID, URL, TLM and EID frames as beacons.eddystone, one flat shape with frameType saying which fields are set. It needs a listener too.
Broadcasting
POST beacons/broadcast/ibeacon, …/eddystone-uid or …/eddystone-url starts advertising and replaces any advertisement already running. A URL too long for an Eddystone-URL frame answers 400. Apple platforms stop the advertisement in the background.
Platform setup
Shiny.Beacons’. On iOS and Mac Catalyst: NSLocationWhenInUseUsageDescription for ranging, NSLocationAlwaysAndWhenInUseUsageDescription for monitoring, and NSBluetoothAlwaysUsageDescription for Eddystone and broadcasting. On Android: BLUETOOTH_SCAN and BLUETOOTH_CONNECT (location before Android 12), BLUETOOTH_ADVERTISE to broadcast, and FOREGROUND_SERVICE_CONNECTED_DEVICE with POST_NOTIFICATIONS to monitor. Linux answers 501.
Ask for the feature you need, listen, then start ranging a region. Ranging reports every beacon in range about once
a second while the page is open; monitoring reports entering and leaving a region, and reaches background.js too.
import { BeaconsBridge } from "@shinyorg/appdevicebridge" ;
const beacons = new BeaconsBridge ();
const { access } = await beacons. requestAccess ({ feature: "Ranging" });
if (access !== "Available" ) return ;
await beacons. onBeacon ( b => console. log (b.major, b.minor, b.proximity, `${ b . distance . toFixed ( 1 ) } m` ));
await beacons. startRanging ({
uuid: "B9407F30-F5F8-466E-AFF9-25556B57FE6D" ,
await beacons. stopRanging ( "store" );
GET /_bridge/ beacons/statusThe permission state of each feature, and whether the device is broadcasting. POST /_bridge/ beacons/accessRequests the permission a feature needs — background location for monitoring on Apple platforms. GET /_bridge/ beacons/rangingThe regions being ranged. POST /_bridge/ beacons/rangingStarts ranging a region; readings arrive through onBeacon, so listen first: without a listener it fails with 409. DELETE /_bridge/ beacons/rangingStops ranging every region. DELETE /_bridge/ beacons/ranging/{identifier} Stops ranging one region. GET /_bridge/ beacons/regionsThe regions being monitored. POST /_bridge/ beacons/regionsStarts monitoring a region; transitions arrive through onRegion while a page is open, and to the web app's beacon handler otherwise. DELETE /_bridge/ beacons/regionsStops monitoring every region. DELETE /_bridge/ beacons/regions/{identifier} Stops monitoring one region. GET /_bridge/ beacons/regions/{identifier} /stateWhether the device is inside a monitored region now. POST /_bridge/ beacons/eddystoneStarts scanning for Eddystone frames; they arrive through onEddystone, so listen first: without a listener it fails with 409. DELETE /_bridge/ beacons/eddystoneStops scanning for Eddystone frames. GET /_bridge/ beacons/broadcastWhether the device is advertising as a beacon. POST /_bridge/ beacons/broadcast/ibeaconAdvertises as an iBeacon, replacing any running advertisement. POST /_bridge/ beacons/broadcast/eddystone-uidAdvertises an Eddystone-UID frame, replacing any running advertisement. POST /_bridge/ beacons/broadcast/eddystone-urlAdvertises an Eddystone-URL frame, replacing any running advertisement. DELETE /_bridge/ beacons/broadcastStops advertising. GET /_bridge/ beacons/status# The permission state of each feature, and whether the device is broadcasting.
C# IBeaconsBridge.GetStatusAsyncTS BeaconsBridge.getStatus
Response 200 BeaconsStatus BeaconsStatus · 5 fields rangingAccessState monitoringAccessState eddystoneAccessState broadcastingSupportedboolean Whether the app registered broadcasting and the platform has it. broadcastingboolean Whether the device is advertising as a beacon now.
POST /_bridge/ beacons/access# Requests the permission a feature needs — background location for monitoring on Apple platforms.
C# IBeaconsBridge.RequestAccessAsyncTS BeaconsBridge.requestAccess
Body application/json · BeaconAccessRequest BeaconAccessRequest · 1 field Response 200 BeaconAccessResult BeaconAccessResult · 1 field GET /_bridge/ beacons/ranging# The regions being ranged.
C# IBeaconsBridge.GetRangedRegionsAsyncTS BeaconsBridge.getRangedRegions
Response 200 BeaconRegion []BeaconRegion · 6 fields identifierstring Names the region; ranging or monitoring one with an identifier already in use replaces it. uuidstring majornumber | null minornumber | null Needs major. notifyOnEntryboolean Monitoring only. notifyOnExitboolean Monitoring only.
POST /_bridge/ beacons/ranging# Starts ranging a region; readings arrive through onBeacon, so listen first: without a listener it fails with 409. Several regions can be ranged at once. Ranging is a foreground activity, and it stops once nothing listens to it.
C# IBeaconsBridge.StartRangingAsyncTS BeaconsBridge.startRanging
Body application/json · BeaconRegion BeaconRegion · 6 fields identifierstring Names the region; ranging or monitoring one with an identifier already in use replaces it. uuidstring majornumber | null minornumber | null Needs major. notifyOnEntryboolean Monitoring only. notifyOnExitboolean Monitoring only.
Response 204 No contentErrors 409 ConflictDELETE /_bridge/ beacons/ranging# Stops ranging every region.
C# IBeaconsBridge.StopAllRangingAsyncTS BeaconsBridge.stopAllRanging
Response 204 No contentDELETE /_bridge/ beacons/ranging/{identifier} # Stops ranging one region.
C# IBeaconsBridge.StopRangingAsyncTS BeaconsBridge.stopRanging
Parameter In Type identifierpath string required
Response 204 No contentGET /_bridge/ beacons/regions# The regions being monitored. Monitoring survives the app restarting.
C# IBeaconsBridge.GetMonitoredRegionsAsyncTS BeaconsBridge.getMonitoredRegions
Response 200 BeaconRegion []BeaconRegion · 6 fields identifierstring Names the region; ranging or monitoring one with an identifier already in use replaces it. uuidstring majornumber | null minornumber | null Needs major. notifyOnEntryboolean Monitoring only. notifyOnExitboolean Monitoring only.
POST /_bridge/ beacons/regions# Starts monitoring a region; transitions arrive through onRegion while a page is open, and to the web app's beacon handler otherwise. Fails with 409 without permission.
C# IBeaconsBridge.StartMonitoringAsyncTS BeaconsBridge.startMonitoring
Body application/json · BeaconRegion BeaconRegion · 6 fields identifierstring Names the region; ranging or monitoring one with an identifier already in use replaces it. uuidstring majornumber | null minornumber | null Needs major. notifyOnEntryboolean Monitoring only. notifyOnExitboolean Monitoring only.
Response 204 No contentErrors 409 ConflictDELETE /_bridge/ beacons/regions# Stops monitoring every region.
C# IBeaconsBridge.StopAllMonitoringAsyncTS BeaconsBridge.stopAllMonitoring
Response 204 No contentDELETE /_bridge/ beacons/regions/{identifier} # Stops monitoring one region.
C# IBeaconsBridge.StopMonitoringAsyncTS BeaconsBridge.stopMonitoring
Parameter In Type identifierpath string required
Response 204 No contentGET /_bridge/ beacons/regions/{identifier} /state# Whether the device is inside a monitored region now. Fails with 404 for a region not monitored.
C# IBeaconsBridge.GetRegionStateAsyncTS BeaconsBridge.getRegionState
Parameter In Type identifierpath string required
Response 200 BeaconRegionStatus BeaconRegionStatus · 2 fields Errors 404 Not foundPOST /_bridge/ beacons/eddystone# Starts scanning for Eddystone frames; they arrive through onEddystone, so listen first: without a listener it fails with 409. The scan stops once nothing listens to it.
C# IBeaconsBridge.StartEddystoneScanAsyncTS BeaconsBridge.startEddystoneScan
Response 204 No contentErrors 409 ConflictDELETE /_bridge/ beacons/eddystone# Stops scanning for Eddystone frames.
C# IBeaconsBridge.StopEddystoneScanAsyncTS BeaconsBridge.stopEddystoneScan
Response 204 No contentGET /_bridge/ beacons/broadcast# Whether the device is advertising as a beacon.
C# IBeaconsBridge.GetBroadcastAsyncTS BeaconsBridge.getBroadcast
Response 200 BeaconBroadcastStatus BeaconBroadcastStatus · 1 field POST /_bridge/ beacons/broadcast/ibeacon# Advertises as an iBeacon, replacing any running advertisement. Apple platforms stop it in the background.
C# IBeaconsBridge.BroadcastIBeaconAsyncTS BeaconsBridge.broadcastIBeacon
Body application/json · BeaconBroadcast BeaconBroadcast · 4 fields uuidstring majornumber minornumber txPowernumber | null The power a receiver sees at one meter, dBm; -59 when null. It calibrates distance and does not change the radio.
Response 204 No contentPOST /_bridge/ beacons/broadcast/eddystone-uid# Advertises an Eddystone-UID frame, replacing any running advertisement.
C# IBeaconsBridge.BroadcastEddystoneUidAsyncTS BeaconsBridge.broadcastEddystoneUid
Body application/json · EddystoneUidBroadcast EddystoneUidBroadcast · 3 fields namespacestring 10 bytes, hex. instancestring 6 bytes, hex. txPowernumber | null The power at zero meters, dBm; -18 when null.
Response 204 No contentPOST /_bridge/ beacons/broadcast/eddystone-url# Advertises an Eddystone-URL frame, replacing any running advertisement. Fails with 400 for a URL too long to encode.
C# IBeaconsBridge.BroadcastEddystoneUrlAsyncTS BeaconsBridge.broadcastEddystoneUrl
Body application/json · EddystoneUrlBroadcast EddystoneUrlBroadcast · 2 fields urlstring Must compress to 17 bytes or fewer under Eddystone's URL encoding. txPowernumber | null The power at zero meters, dBm; -18 when null.
Response 204 No contentErrors 400 Bad requestDELETE /_bridge/ beacons/broadcast# Stops advertising.
C# IBeaconsBridge.StopBroadcastAsyncTS BeaconsBridge.stopBroadcast
Response 204 No content
An iBeacon seen while ranging.
C# IBeaconsBridge.OnBeaconAsyncTS BeaconsBridge.onBeacon
Payload BeaconReading regionstring The identifier of the ranged region it belongs to. uuidstring majornumber minornumber rssinumber dBm. proximityBeaconProximity distancenumber Estimated meters. txPowernumber | null The beacon's calibrated power at one meter. Null on Apple platforms, where CoreLocation hides it. timestampstring
The device entered or left a monitored region, while the page is open.
C# IBeaconsBridge.OnRegionAsyncTS BeaconsBridge.onRegion
Payload BeaconRegionStatus An Eddystone frame seen while scanning.
C# IBeaconsBridge.OnEddystoneAsyncTS BeaconsBridge.onEddystone
Payload EddystoneFrame frameTypeEddystoneFrameType peripheralIdstring The advertising device: a GUID on Apple platforms, a MAC address on Android. rssinumber timestampstring namespacestring | null 10 bytes, hex. instancestring | null 6 bytes, hex. urlstring | null ephemeralIdstring | null Hex. txPowernumber | null distancenumber | null proximityBeaconProximity | null batteryVoltsnumber | null temperatureCelsiusnumber | null advertisementCountnumber | null uptimestring | null Telemetry: time since the beacon powered up. encryptedboolean | null
JSON property names are camelCase and enums travel as their names. A ? marks a property the payload may omit;| null one that may be null.
The permission state of each feature, without prompting. A feature the app did not register is NotSupported.
rangingAccessState monitoringAccessState eddystoneAccessState broadcastingSupportedboolean Whether the app registered broadcasting and the platform has it. broadcastingboolean Whether the device is advertising as a beacon now.
Where a permission stands. Reported by every bridge that needs one; the names match Shiny's own.
"Unknown""NotSupported""NotSetup""Disabled""Restricted""Denied""Available"
BeaconAccessRequestobject # A part of the beacons bridge, each with its own permission and registration.
"Ranging""Monitoring""Eddystone""Broadcasting"
BeaconAccessResultobject # A set of iBeacons, by UUID and optionally narrowed by major and then minor.
identifierstring Names the region; ranging or monitoring one with an identifier already in use replaces it. uuidstring majornumber | null minornumber | null Needs major. notifyOnEntryboolean Monitoring only. notifyOnExitboolean Monitoring only.
BeaconRegionStatusobject # "Unknown""Entered""Exited"
BeaconBroadcastStatusobject # Advertising as an iBeacon.
uuidstring majornumber minornumber txPower?number | null The power a receiver sees at one meter, dBm; -59 when null. It calibrates distance and does not change the radio.
EddystoneUidBroadcastobject # namespacestring 10 bytes, hex. instancestring 6 bytes, hex. txPower?number | null The power at zero meters, dBm; -18 when null.
EddystoneUrlBroadcastobject # urlstring Must compress to 17 bytes or fewer under Eddystone's URL encoding. txPower?number | null The power at zero meters, dBm; -18 when null.
One observation of an iBeacon while ranging.
regionstring The identifier of the ranged region it belongs to. uuidstring majornumber minornumber rssinumber dBm. proximityBeaconProximity distancenumber Estimated meters. txPowernumber | null The beacon's calibrated power at one meter. Null on Apple platforms, where CoreLocation hides it. timestampstring
A distance bucket derived from the estimated distance.
"Unknown""Immediate""Near""Far"
An Eddystone frame. Which fields are set depends on frameType: Uid has namespace and instance, Url has url, Eid has ephemeralId, and those three have txPower, distance and proximity. Tlm is the beacon's telemetry.
frameTypeEddystoneFrameType peripheralIdstring The advertising device: a GUID on Apple platforms, a MAC address on Android. rssinumber timestampstring namespacestring | null 10 bytes, hex. instancestring | null 6 bytes, hex. urlstring | null ephemeralIdstring | null Hex. txPowernumber | null distancenumber | null proximityBeaconProximity | null batteryVoltsnumber | null temperatureCelsiusnumber | null advertisementCountnumber | null uptimestring | null Telemetry: time since the beacon powered up. encryptedboolean | null
"Uid""Url""Tlm""Eid"
These routes sit under AppDeviceBridgePolicies.Bridges like every other bridge: callers on this device by default.
See Security .