There has been some discussion about support for the geolocation APIs from within service workers (and more general in the background), which made me wonder what some of the possible solutions for background access to geolocation information might look like in a potential future generic sensor API based geolocation API. Especially since it seems like a lot of this potential functionality would be useful for any sensor, not just geolocation.
So roughly the use cases I see for accessing a sensor from a service worker:
- Read the current (or recently cached) value from some sensor. In case of geolocation to get the current location in response to some other event.
- Log data from a sensor over some time period. For geolocation this would allow building apps like fitness trackers, where the app will want to start/stop recording location information, but doesn’t need to be constantly woken up while recording (unless the app is awake/visible anyway).
- Get notified when some sensor enters/leaves some range, some kind of sensor triggered alarms. For geolocation this would be the Geofencing API, but I think these kinds of triggers would make sense for many different types of sensors.
The first of these use cases is probably addressed sufficiently by the existing API in the form of Sensor.requestReading()
and/or Sensor.reading
, but the other two use cases don’t seem to really be supported by the currently proposed generic sensor API.
For the second use case some kind of Sensor.startRecording()
method might make sense, which could be used something like this:
// start recording a track, from a webapp
navigator.serviceWorker.ready.then(registration => {
registration.sensors.startRecording(new GeolocationSensor({frequency: 0.1}), {tag: 'some identifier'})
.then(recording => {
// recording somehow represents the recording in progress
}).catch(e => {
// failed to start recording the sensor
});
});
// check up on a track that is currently being recorded
navigator.serviceWorker.ready.then(registration => {
registration.sensors.getRecording({tag: 'some identifier'})
.then(recording => {
recording.isActive.then(active => console.log('Recording is active: ' + active));
recording.getReadings({/* options */}).then(readings => {
// one possible option to getReadings could be to clear the readings that
// are returned from the recording
console.log('Logged track so far:');
readings.forEach(reading => console.log(reading));
});
});
});
// stop recording for a particular track
navigator.serviceWorker.ready.then(registration => {
registration.sensors.getRecording({tag: 'some identifier'})
.then(recording => recording.stop())
.then(readings => {
readings.forEach(reading => console.log(reading));
});
});
// in the service worker, get signalled about errors with the recording
self.addEventListener('sensorRecordingError', function(e) {
console.log('Problems in recording ' + e.recording.tag);
console.log('Which is recording sensor ' + e.recording.sensor.sensorId);
e.recording.getReadings().then(readings => console.log('Logged ' + readings.length + ' readings'));
};
For the geofencing/triggers use cases, I imagine we'd let specs defining concrete Sensors also define specific triggers. These triggers can probably be either absolute (when the value enters/leaves this range) or relative (when the value changes by this much). In case of geolocation the triggers would be geographic regions as currently defined in the geofencing spec. In some sample code this might look something like this:
// Monitor a sensor:
navigator.serviceWorker.ready.then(registration => {
registration.sensors.addTrigger(new GeolocationSensor(), {
name: 'myfence',
latitude: 37.421999,
longitude: -122.084015,
radius: 1000
}).then(sensorTrigger => console.log('Now monitoring geofence'))
.catch(e => console.log(e));
// or maybe a different API:
new GeolocationSensor().addTrigger({name: 'foo', ...}).then(...);
});
// respond to a trigger being triggered, in a SW
self.addEventListener('sensortrigger', function(e) {
console.log('Triggered: ' + e.trigger.name);
console.log('Type of trigger: ' + e.triggerType); /* 'enter' or 'leave' */
// e.trigger.sensor is the sensor that was triggered
// Maybe unregister the trigger
if (shouldUnregister(e.trigger)) {
e.waitUntil(e.trigger.remove());
}
});
// deal with errors with the trigger, in a SW
self.addEventListener('sensortriggererror', function(e) {
console.log(e.trigger.name);
console.log(e.trigger.sensor.sensorId);
console.log(e.error);
});
// Look up existing registered triggers
navigator.serviceWorker.ready.then(registration => {
registration.sensors.getTriggers({name: 'myfence'}).then(
triggers => triggers.forEach(trigger => console.log(trigger)));
registration.sensors.getTriggers({sensor: new GeolocationSensor()}).then(
triggers => triggers.forEach(trigger => console.log(trigger)));
// etc
});
Excellent!
Can't wait for this kind of functionality.
Basically being able to continue to receive and act on position (lat/lon) updates when the mobile device is currently on another web page or temporarily on another app.
With Geofencing gone in hibernation is there a group working on this?