Skip to main content

Geofences

Geofences are circular zones defined by a center point and radius. When a GPS point is inserted, the API checks all active geofences and emits WebSocket events when a device enters or exits a zone.

Geofence Fields

FieldTypeDescription
idUUIDGeofence ID
namestringDisplay name
descriptionstring?Optional description
latitudefloatCenter latitude
longitudefloatCenter longitude
radius_kmfloatTrigger radius in kilometers
is_activeboolInactive geofences are not checked
metadataJSONB?Arbitrary module-specific data

REST Endpoints

MethodPathPermissionDescription
GET/v1/geofencesgeofences:readList geofences
GET/v1/geofences/{id}geofences:readGet geofence
POST/v1/geofencesgeofences:writeCreate geofence
PATCH/v1/geofences/{id}geofences:writeUpdate geofence
DELETE/v1/geofences/{id}geofences:deleteDelete geofence

GraphQL

  • geofences(page, limit)PaginatedGeofences
  • geofence(id)GqlGeofence
  • createGeofence(input)GqlGeofence
  • updateGeofence(id, input)GqlGeofence
  • deleteGeofence(id)Boolean

Create Geofence

POST /v1/geofences
{
"name": "Schleuse Feudenheim",
"latitude": 49.4875,
"longitude": 8.5370,
"radius_km": 0.5,
"metadata": { "type": "lock", "waterway": "Neckar" }
}

Proximity Check

On every POST /v1/gps insert, the API automatically runs a geofence proximity check as a side-effect in the create_gps_data handler (see check_geofences in crates/heimdall-rest/src/handlers/gps.rs). It computes the Haversine distance from the new GPS point to each active geofence center. Geofences within radius_km trigger an event.

The API tracks the last-known inside/outside state per (device_id, geofence_id) pair in memory using a GeofenceState (RwLock<HashMap>). This allows detecting enter (was outside, now inside) and exit (was inside, now outside) transitions rather than firing on every point.

In-Memory State is Volatile

GeofenceState is held in application memory and is not persisted. On API restart, all geofence state is lost. The first GPS point received after a restart will not detect enter/exit transitions because there is no previous state to compare against -- the state defaults to "outside" for all pairs. Transitions resume correctly from the second point onward once state is re-established.

WebSocket Events

Events are broadcast on the "geofence" channel.

interface GeofenceEventMessage {
type: 'geofenceEvent';
data: {
geofence_id: string;
geofence_name: string;
event: 'enter' | 'exit';
device_id: string | null;
distance_km: number;
metadata: any;
};
}

Subscribe to channel "geofence" alongside "gps" to receive both streams.

RBAC Permissions

PermissionDescription
geofences:readView geofences
geofences:writeCreate and update geofences
geofences:deleteDelete geofences

Scope

The current implementation covers CRUD and proximity checks. Polygon geofences and OBS overlay integrations (Schleusen-Alert) are out of scope for this release.