/**
* Class template
* @module j5e/thermometer
* @requires module:j5e/withinable
* @requires module:j5e/fn
*/
import Sensor from "j5e/sensor";
import { normalizeDevice, normalizeIO, toFixed } from "j5e/fn";
/**
* Class representing a generic thermistor
* @classdesc The Thermometer class allows for control of analog thermistors
* @async
* @inheritdoc
* @extends module:j5e/sensor~Sensor
* @fires data
* @fires change
*/
class Thermometer extends Sensor {
static CELSIUS_TO_KELVIN = 273.15;
/**
* Instantiate a Thermometer
* @param {number|string|object} io - Pin identifier or IO Options (See {@tutorial C-INSTANTIATING})
* @example
* <caption>Use a thermometer</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer(12);
*
* @example
* <caption>Pass in a custom toCelsius function</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer({
* pin: 12
* });
*/
constructor(io) {
return (async() => {
io = normalizeIO(io);
const sensor = await super(io);
sensor.configure();
return sensor;
})();
}
/**
* Configure a Thermometer
* @returns {Thermometer} The instance on which the method was called
* @param {object} options - Device configuration options
* @param {number} [options.aref=3.3] - Analog reference voltage
* @param {boolean} [options.enabled=true] - Wether the device is currently performing reads every <interval>ms
* @param {number} [options.interval=100] - Interval between readings in millseconds
* @param {number[]} [options.limit=null] - Limit the output range
* @param {number[]} [options.range=[0, N]] - The input range of the sensor
* @param {number[]} [options.scale=[0, N]] - The output range for the sensor's value
* @param {number} [options.threshold=1] - The minimum amount of change required to emit a "change" event
* @param {callback} [options.toCelsius] - Function that converts raw value to Celsius
* @example
* <caption>Passing in Cofiguration Options</caption>
import Thermometer from "j5e/thermometer";
*
* const thermometer = await new Thermometer({
* pin: 14
* });
*
* myThermometer.configure({
* toCelsius: function(raw) {
* return raw / 16;
* }
* }) ;
*
* thermometer.on("change", data => {
* trace(thermometer.celsius);
* });
*/
configure(options = {}) {
options = normalizeDevice(options);
super.configure(options);
if (options.toCelsius) {
this.customToCelsius = options.toCelsius;
}
return this;
}
/**
* Get degrees in celsius
* @type {number}
* @readonly
* @example
* <caption>Get degrees in celsius in action</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer(12);
* myThermometer.on("change", function() {
* console.log(myThermometer.celsius);
*/
get celsius() {
if (this.customToCelsius) {
return this.customToCelsius(this.median || this.value);
} else {
return this.toCelsius(this.median || this.value);
}
}
/**
* Alias for celsius
* @type {number}
* @readonly
* @example
* <caption>Get degrees in celsius in action</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer(12);
* myThermometer.on("change", function() {
* console.log(myThermometer.C);
*/
get C() {
return this.celsius;
}
/**
* Get degrees in fahrenheit
* @type {number}
* @readonly
* @example
* <caption>Get degrees in fahrenheit in action</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer(12);
* myThermometer.on("change", function() {
* console.log(myThermometer.fahrenheit);
*/
get fahrenheit() {
return toFixed((this.celsius * 9 / 5) + 32, 2);
}
/**
* Alias for fahrenheit
* @type {number}
* @readonly
* @example
* <caption>Get degrees in fahrenheit in action</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer(12);
* myThermometer.on("change", function() {
* console.log(myThermometer.F);
*/
get F() {
return this.fahrenheit;
}
/**
* Get degrees in kelvin
* @type {number}
* @readonly
* @example
* <caption>Get degrees in kelvin in action</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer(12);
* myThermometer.on("change", function() {
* console.log(myThermometer.kelvin);
*/
get kelvin() {
return toFixed(this.celsius + Thermometer.CELSIUS_TO_KELVIN, 2);
}
/**
* Alias for kelvin
* @type {number}
* @readonly
* @example
* <caption>Get degrees in kelvin in action</caption>
* import Thermometer from "j5e/thermometer";
*
* const myThermometer = await new Thermometer(12);
* myThermometer.on("change", function() {
* console.log(myThermometer.K);
*/
get K() {
return this.kelvin;
}
/**
* Convert a raw reading to Celsius
* param {number} value
* @private
*/
toCelsius(value) {
return value;
}
/**
* Internal method for processing reads
* @access private
*/
emitEvents() {
let boundary;
const data = {};
data.C = data.celsius = this.celsius;
data.F = data.fahrenheit = this.fahrenheit;
data.K = data.kelvin = this.kelvin;
data.raw = this.raw;
this.emit("data", data);
if (typeof this.last === "undefined") {
this.last = this.median;
}
// If the filtered (#state.median) value for this interval is at least ± the
// configured threshold from last, fire change events
if (this.median <= (this.last - this.threshold) || this.median >= (this.last + this.threshold)) {
this.emit("change", data);
// Update the instance-local `last` value (only) when a new change event
// has been emitted. For comparison in the next interval
this.last = this.median;
}
if (this.limit) {
if (this.median <= this.limit[0]) {
boundary = "lower";
}
if (this.median >= this.limit[1]) {
boundary = "upper";
}
if (boundary) {
this.emit("limit", {
boundary,
value: this.median
});
this.emit(`limit:${boundary}`, this.median);
}
}
}
}
export default Thermometer;