|
(function() { |
|
// The WeakMap is used to represent a backing state mechanism. |
|
var priv = new WeakMap(); |
|
|
|
function Sensor(opts, key) { |
|
if (key !== priv) { |
|
throw new Error("Illegal Constructor"); |
|
} |
|
|
|
priv.set(this, { |
|
value: null, |
|
range: opts.range, |
|
frequency: opts.frequency || 100 /* whatever, just a reasonable default */ |
|
}); |
|
|
|
// This is only used by the simulator code |
|
// and will negate the benefits of the WeakMap. |
|
// Add to simulated system device sensor list. |
|
sensors.push({ |
|
device: this, |
|
timestamp: Date.now() |
|
}); |
|
} |
|
|
|
Sensor.prototype = { |
|
constructor: Sensor, |
|
get range() { |
|
return priv.get(this).range.slice(); |
|
}, |
|
get value() { |
|
return priv.get(this).value; |
|
}, |
|
}; |
|
|
|
/* |
|
Sensor.Temperature |
|
|
|
Celcius, Fahrenheit |
|
|
|
@param opts object { unit: C | F } |
|
*/ |
|
|
|
Sensor.Temperature = function(opts) { |
|
opts = opts || {}; |
|
Sensor.call(this, { |
|
range: Sensor.Temperature.C.slice(), |
|
frequency: opts.frequency |
|
}, priv); |
|
|
|
opts = opts || {}; |
|
|
|
var unit = opts.unit || "C"; |
|
|
|
this.unit(unit); |
|
}; |
|
|
|
Object.defineProperties(Sensor.Temperature, { |
|
C: { |
|
value: [-40, 150], |
|
writable: false, |
|
configurable: false, |
|
enumerable: true |
|
}, |
|
F: { |
|
value: [-40, 302], |
|
writable: false, |
|
configurable: false, |
|
enumerable: true |
|
} |
|
}); |
|
|
|
Sensor.Temperature.prototype = Object.create(Sensor.prototype, { |
|
constructor: Sensor.Temperature, |
|
unit: { |
|
value: function(unit) { |
|
var range = Sensor.Temperature[(unit + "").toUpperCase() || "C"]; |
|
|
|
if (range === void 0) { |
|
throw new Error("Invalid Temperature unit"); |
|
} |
|
|
|
var state = priv.get(this); |
|
|
|
// This is not really what a unit mechanism would do, |
|
// this only for illustrative purposes. |
|
state.range[0] = range[0]; |
|
state.range[1] = range[1]; |
|
|
|
return this; |
|
} |
|
} |
|
}); |
|
|
|
/* |
|
Sensor.Light |
|
|
|
Lux |
|
|
|
@param opts object { range: [ lower, upper ] } |
|
*/ |
|
|
|
Sensor.Light = function(opts) { |
|
opts = opts || {}; |
|
Sensor.call(this, { |
|
range: [0, 100000], |
|
frequency: opts.frequency |
|
}, priv); |
|
}; |
|
|
|
Sensor.Light.prototype = Object.create(Sensor.prototype, { |
|
constructor: Sensor.Light |
|
}); |
|
|
|
/* |
|
Sensor.Proximity |
|
|
|
cm |
|
|
|
@param opts object { range: [ lower, upper ] } |
|
*/ |
|
|
|
Sensor.Proximity = function(opts) { |
|
opts = opts || {}; |
|
Sensor.call(this, { |
|
// 5 feet, in cm |
|
range: [0, 152.4], |
|
frequency: opts.frequency |
|
}, priv); |
|
}; |
|
|
|
Sensor.Proximity.prototype = Object.create(Sensor.prototype, { |
|
constructor: Sensor.Proximity |
|
}); |
|
|
|
|
|
// Expose the Sensor API. |
|
(window || global).Sensor = Sensor; |
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
// This part simulates a platform operation that is NOT |
|
// observable from by user program code. It is written |
|
// in a way that will the reader of this example to |
|
// run the code in a browser to witness the simulation. |
|
// |
|
// THIS |
|
// |
|
// IS |
|
// |
|
// MEANT |
|
// |
|
// TO |
|
// |
|
// SIMULATE |
|
// |
|
// IPC |
|
// |
|
// |
|
var sensors = []; |
|
setInterval(function() { |
|
sensors.forEach(function(registered) { |
|
var sensor = registered.device; |
|
var state = priv.get(sensor); |
|
var min = state.range[0]; |
|
var max = state.range[1]; |
|
var totallyMadeUpValue = Math.floor(Math.random() * (max - min + 1)) + min; |
|
|
|
// Sensor readings are delivered per the frequency param value |
|
var now = Date.now(); |
|
var changeRecord; |
|
|
|
// This is only to simulate the behaviour of a controlled frequency |
|
if (now >= (sensor.timestamp + state.frequency) || state.value === null) { |
|
sensor.timestamp = now; |
|
|
|
state.value = totallyMadeUpValue; |
|
|
|
changeRecord = { |
|
timestamp: now, |
|
value: totallyMadeUpValue |
|
}; |
|
|
|
if (typeof sensor.onchange === "function") { |
|
sensor.onchange.call(sensor, changeRecord); |
|
} |
|
} |
|
}); |
|
}, 10); |
|
// End simulation mechanism |
|
|
|
|
|
// IIFE used to simulate the platform's "setup" needs |
|
}()); |
|
|
|
// ------------------------------------------------------------------------------ |
|
// User code... |
|
|
|
var illegal; |
|
|
|
// Just a test of the base Sensor |
|
try { |
|
illegal = new Sensor(); |
|
} catch (e) { |
|
console.log(e.message === "Illegal Constructor"); |
|
} |
|
|
|
var temp = new Sensor.Temperature({ unit: "C" }); |
|
console.log("temp", temp); |
|
|
|
temp.onchange = function() { |
|
console.log("temp change", this.value); |
|
}; |
|
|
|
var light = new Sensor.Light(); |
|
console.log("light", light); |
|
|
|
light.onchange = function() { |
|
console.log("light change", this.value); |
|
}; |
|
|
|
var prox = new Sensor.Proximity(); |
|
console.log("prox", prox); |
|
|
|
prox.onchange = function() { |
|
console.log("prox change", this.value); |
|
}; |
|
|
|
|
|
// And, the non-event based access... |
|
// |
|
requestAnimationFrame(function frame() { |
|
requestAnimationFrame(frame); |
|
|
|
// these might very well be null until the value is |
|
// delivered, but that's totall ok. |
|
|
|
if (temp.value !== null) { |
|
console.log("celcius: ", temp.value); |
|
} |
|
|
|
if (light.value !== null) { |
|
console.log("lux: ", light.value); |
|
} |
|
|
|
if (prox.value !== null) { |
|
console.log("cm: ", prox.value); |
|
} |
|
}); |
|
|
|
// No need for instances in the one-time and done case: |
|
// |
|
Sensor.Temperature.currentValue().then(data => ...); |
|
|
|
|