Skip to content
Shiny.NET

Beacons

NuGet package Shiny.AppDeviceBridge.Beacons

The beacons bridge ranges and monitors iBeacons, scans Eddystone frames and broadcasts the device as a beacon, backed by Shiny.Beacons.

Terminal window
dotnet add package Shiny.AppDeviceBridge.Beacons
builder.UseAppDeviceBridge(
bridge => bridge.AddBeaconsBridge(),
webApp => { /* … */ }
);
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({
identifier: "store",
uuid: "B9407F30-F5F8-466E-AFF9-25556B57FE6D",
major: null,
minor: null,
notifyOnEntry: true,
notifyOnExit: true
});
// later
await beacons.stopRanging("store");
GET/_bridge/beacons/status#

The permission state of each feature, and whether the device is broadcasting.

C#IBeaconsBridge.GetStatusAsyncTSBeaconsBridge.getStatus
Response
200 BeaconsStatus
BeaconsStatus · 5 fields
rangingAccessState
monitoringAccessState
eddystoneAccessState
broadcastingSupportedbooleanWhether the app registered broadcasting and the platform has it.
broadcastingbooleanWhether 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.RequestAccessAsyncTSBeaconsBridge.requestAccess
Body
application/json · BeaconAccessRequest
BeaconAccessRequest · 1 field
featureBeaconFeature
Response
200 BeaconAccessResult
BeaconAccessResult · 1 field
accessAccessState
GET/_bridge/beacons/ranging#

The regions being ranged.

C#IBeaconsBridge.GetRangedRegionsAsyncTSBeaconsBridge.getRangedRegions
Response
200 BeaconRegion[]
BeaconRegion · 6 fields
identifierstringNames the region; ranging or monitoring one with an identifier already in use replaces it.
uuidstring
majornumber | null
minornumber | nullNeeds major.
notifyOnEntrybooleanMonitoring only.
notifyOnExitbooleanMonitoring 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.StartRangingAsyncTSBeaconsBridge.startRanging
Body
application/json · BeaconRegion
BeaconRegion · 6 fields
identifierstringNames the region; ranging or monitoring one with an identifier already in use replaces it.
uuidstring
majornumber | null
minornumber | nullNeeds major.
notifyOnEntrybooleanMonitoring only.
notifyOnExitbooleanMonitoring only.
Response
204 No content
Errors
409Conflict
DELETE/_bridge/beacons/ranging#

Stops ranging every region.

C#IBeaconsBridge.StopAllRangingAsyncTSBeaconsBridge.stopAllRanging
Response
204 No content
DELETE/_bridge/beacons/ranging/{identifier}#

Stops ranging one region.

C#IBeaconsBridge.StopRangingAsyncTSBeaconsBridge.stopRanging
ParameterInType
identifierpathstringrequired
Response
204 No content
GET/_bridge/beacons/regions#

The regions being monitored. Monitoring survives the app restarting.

C#IBeaconsBridge.GetMonitoredRegionsAsyncTSBeaconsBridge.getMonitoredRegions
Response
200 BeaconRegion[]
BeaconRegion · 6 fields
identifierstringNames the region; ranging or monitoring one with an identifier already in use replaces it.
uuidstring
majornumber | null
minornumber | nullNeeds major.
notifyOnEntrybooleanMonitoring only.
notifyOnExitbooleanMonitoring 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.StartMonitoringAsyncTSBeaconsBridge.startMonitoring
Body
application/json · BeaconRegion
BeaconRegion · 6 fields
identifierstringNames the region; ranging or monitoring one with an identifier already in use replaces it.
uuidstring
majornumber | null
minornumber | nullNeeds major.
notifyOnEntrybooleanMonitoring only.
notifyOnExitbooleanMonitoring only.
Response
204 No content
Errors
409Conflict
DELETE/_bridge/beacons/regions#

Stops monitoring every region.

C#IBeaconsBridge.StopAllMonitoringAsyncTSBeaconsBridge.stopAllMonitoring
Response
204 No content
DELETE/_bridge/beacons/regions/{identifier}#

Stops monitoring one region.

C#IBeaconsBridge.StopMonitoringAsyncTSBeaconsBridge.stopMonitoring
ParameterInType
identifierpathstringrequired
Response
204 No content
GET/_bridge/beacons/regions/{identifier}/state#

Whether the device is inside a monitored region now. Fails with 404 for a region not monitored.

C#IBeaconsBridge.GetRegionStateAsyncTSBeaconsBridge.getRegionState
ParameterInType
identifierpathstringrequired
Response
200 BeaconRegionStatus
BeaconRegionStatus · 2 fields
identifierstring
stateBeaconRegionState
Errors
404Not found
POST/_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.StartEddystoneScanAsyncTSBeaconsBridge.startEddystoneScan
Response
204 No content
Errors
409Conflict
DELETE/_bridge/beacons/eddystone#

Stops scanning for Eddystone frames.

C#IBeaconsBridge.StopEddystoneScanAsyncTSBeaconsBridge.stopEddystoneScan
Response
204 No content
GET/_bridge/beacons/broadcast#

Whether the device is advertising as a beacon.

C#IBeaconsBridge.GetBroadcastAsyncTSBeaconsBridge.getBroadcast
Response
200 BeaconBroadcastStatus
BeaconBroadcastStatus · 1 field
broadcastingboolean
POST/_bridge/beacons/broadcast/ibeacon#

Advertises as an iBeacon, replacing any running advertisement. Apple platforms stop it in the background.

C#IBeaconsBridge.BroadcastIBeaconAsyncTSBeaconsBridge.broadcastIBeacon
Body
application/json · BeaconBroadcast
BeaconBroadcast · 4 fields
uuidstring
majornumber
minornumber
txPowernumber | nullThe power a receiver sees at one meter, dBm; -59 when null. It calibrates distance and does not change the radio.
Response
204 No content
POST/_bridge/beacons/broadcast/eddystone-uid#

Advertises an Eddystone-UID frame, replacing any running advertisement.

C#IBeaconsBridge.BroadcastEddystoneUidAsyncTSBeaconsBridge.broadcastEddystoneUid
Body
application/json · EddystoneUidBroadcast
EddystoneUidBroadcast · 3 fields
namespacestring10 bytes, hex.
instancestring6 bytes, hex.
txPowernumber | nullThe power at zero meters, dBm; -18 when null.
Response
204 No content
POST/_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.BroadcastEddystoneUrlAsyncTSBeaconsBridge.broadcastEddystoneUrl
Body
application/json · EddystoneUrlBroadcast
EddystoneUrlBroadcast · 2 fields
urlstringMust compress to 17 bytes or fewer under Eddystone's URL encoding.
txPowernumber | nullThe power at zero meters, dBm; -18 when null.
Response
204 No content
Errors
400Bad request
DELETE/_bridge/beacons/broadcast#

Stops advertising.

C#IBeaconsBridge.StopBroadcastAsyncTSBeaconsBridge.stopBroadcast
Response
204 No content
EVENTbeacons.ranged#

An iBeacon seen while ranging.

C#IBeaconsBridge.OnBeaconAsyncTSBeaconsBridge.onBeacon
Payload
BeaconReading
regionstringThe identifier of the ranged region it belongs to.
uuidstring
majornumber
minornumber
rssinumberdBm.
proximityBeaconProximity
distancenumberEstimated meters.
txPowernumber | nullThe beacon's calibrated power at one meter. Null on Apple platforms, where CoreLocation hides it.
timestampstring
EVENTbeacons.region#

The device entered or left a monitored region, while the page is open.

C#IBeaconsBridge.OnRegionAsyncTSBeaconsBridge.onRegion
Payload
BeaconRegionStatus
identifierstring
stateBeaconRegionState
EVENTbeacons.eddystone#

An Eddystone frame seen while scanning.

C#IBeaconsBridge.OnEddystoneAsyncTSBeaconsBridge.onEddystone
Payload
EddystoneFrame
frameTypeEddystoneFrameType
peripheralIdstringThe advertising device: a GUID on Apple platforms, a MAC address on Android.
rssinumber
timestampstring
namespacestring | null10 bytes, hex.
instancestring | null6 bytes, hex.
urlstring | null
ephemeralIdstring | nullHex.
txPowernumber | null
distancenumber | null
proximityBeaconProximity | null
batteryVoltsnumber | null
temperatureCelsiusnumber | null
advertisementCountnumber | null
uptimestring | nullTelemetry: 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.

BeaconsStatusobject#

The permission state of each feature, without prompting. A feature the app did not register is NotSupported.

rangingAccessState
monitoringAccessState
eddystoneAccessState
broadcastingSupportedbooleanWhether the app registered broadcasting and the platform has it.
broadcastingbooleanWhether the device is advertising as a beacon now.
AccessStateenum#

Where a permission stands. Reported by every bridge that needs one; the names match Shiny's own.

"Unknown""NotSupported""NotSetup""Disabled""Restricted""Denied""Available"
BeaconFeatureenum#

A part of the beacons bridge, each with its own permission and registration.

"Ranging""Monitoring""Eddystone""Broadcasting"
BeaconRegionobject#

A set of iBeacons, by UUID and optionally narrowed by major and then minor.

identifierstringNames the region; ranging or monitoring one with an identifier already in use replaces it.
uuidstring
majornumber | null
minornumber | nullNeeds major.
notifyOnEntrybooleanMonitoring only.
notifyOnExitbooleanMonitoring only.
BeaconRegionStateenum#
"Unknown""Entered""Exited"
BeaconBroadcastStatusobject#
broadcastingboolean
BeaconBroadcastobject#

Advertising as an iBeacon.

uuidstring
majornumber
minornumber
txPower?number | nullThe power a receiver sees at one meter, dBm; -59 when null. It calibrates distance and does not change the radio.
EddystoneUidBroadcastobject#
namespacestring10 bytes, hex.
instancestring6 bytes, hex.
txPower?number | nullThe power at zero meters, dBm; -18 when null.
EddystoneUrlBroadcastobject#
urlstringMust compress to 17 bytes or fewer under Eddystone's URL encoding.
txPower?number | nullThe power at zero meters, dBm; -18 when null.
BeaconReadingobject#

One observation of an iBeacon while ranging.

regionstringThe identifier of the ranged region it belongs to.
uuidstring
majornumber
minornumber
rssinumberdBm.
proximityBeaconProximity
distancenumberEstimated meters.
txPowernumber | nullThe beacon's calibrated power at one meter. Null on Apple platforms, where CoreLocation hides it.
timestampstring
BeaconProximityenum#

A distance bucket derived from the estimated distance.

"Unknown""Immediate""Near""Far"
EddystoneFrameobject#

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
peripheralIdstringThe advertising device: a GUID on Apple platforms, a MAC address on Android.
rssinumber
timestampstring
namespacestring | null10 bytes, hex.
instancestring | null6 bytes, hex.
urlstring | null
ephemeralIdstring | nullHex.
txPowernumber | null
distancenumber | null
proximityBeaconProximity | null
batteryVoltsnumber | null
temperatureCelsiusnumber | null
advertisementCountnumber | null
uptimestring | nullTelemetry: time since the beacon powered up.
encryptedboolean | null
EddystoneFrameTypeenum#
"Uid""Url""Tlm""Eid"

These routes sit under AppDeviceBridgePolicies.Bridges like every other bridge: callers on this device by default. See Security.