parent
79d339a4d3
commit
2777cd69aa
@ -0,0 +1,15 @@
|
|||||||
|
const CadenceQuality = Object.freeze({
|
||||||
|
HIGH: "high",
|
||||||
|
MEDIUM: "medium",
|
||||||
|
LOW: "low",
|
||||||
|
UNKNOWN: "unknown",
|
||||||
|
});
|
||||||
|
|
||||||
|
function validateCadenceQuality(v) {
|
||||||
|
const set = CadenceQuality;
|
||||||
|
if (!v) return set.UNKNOWN;
|
||||||
|
const vals = Object.values(set);
|
||||||
|
return vals.includes(v) ? v : set.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { validateCadenceQuality, CadenceQuality };
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
import { deepFreeze, isFiniteNumber } from "../../utils";
|
||||||
|
import { CyclingPowerCapabilities } from "./cyclingPower.js";
|
||||||
|
|
||||||
|
export class Control {
|
||||||
|
constructor({
|
||||||
|
supportsERG = false,
|
||||||
|
controlCharacteristic,
|
||||||
|
maxPowerWatts,
|
||||||
|
minPowerWatts,
|
||||||
|
ergRampRateWattsPerSec,
|
||||||
|
cyclingPower,
|
||||||
|
} = {}) {
|
||||||
|
this.supportsERG = Boolean(supportsERG);
|
||||||
|
this.controlCharacteristic = controlCharacteristic;
|
||||||
|
this.maxPowerWatts = isFiniteNumber(maxPowerWatts)
|
||||||
|
? Number(maxPowerWatts)
|
||||||
|
: undefined;
|
||||||
|
this.minPowerWatts = isFiniteNumber(minPowerWatts)
|
||||||
|
? Number(minPowerWatts)
|
||||||
|
: undefined;
|
||||||
|
this.ergRampRateWattsPerSec = isFiniteNumber(ergRampRateWattsPerSec)
|
||||||
|
? Number(ergRampRateWattsPerSec)
|
||||||
|
: undefined;
|
||||||
|
this.cyclingPower =
|
||||||
|
cyclingPower instanceof CyclingPowerCapabilities
|
||||||
|
? cyclingPower
|
||||||
|
: cyclingPower
|
||||||
|
? new CyclingPowerCapabilities(cyclingPower)
|
||||||
|
: undefined;
|
||||||
|
deepFreeze(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
merge = (partial) => new Control(Object.assign({}, this, partial));
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import { deepFreeze } from "../../utils";
|
||||||
|
|
||||||
|
export class Core {
|
||||||
|
constructor({
|
||||||
|
supportsBattery = false,
|
||||||
|
supportsManufacturerData = false,
|
||||||
|
gattServices,
|
||||||
|
gattCharacteristics,
|
||||||
|
manufacturer,
|
||||||
|
model,
|
||||||
|
firmwareVersion,
|
||||||
|
serial,
|
||||||
|
reconnectOnDisconnect = true,
|
||||||
|
requiresPairing = false,
|
||||||
|
} = {}) {
|
||||||
|
this.supportsBattery = Boolean(supportsBattery);
|
||||||
|
this.supportsManufacturerData = Boolean(supportsManufacturerData);
|
||||||
|
this.gattServices = Array.isArray(gattServices)
|
||||||
|
? gattServices.slice()
|
||||||
|
: undefined;
|
||||||
|
this.gattCharacteristics =
|
||||||
|
gattCharacteristics && typeof gattCharacteristics === "object"
|
||||||
|
? Object.assign({}, gattCharacteristics)
|
||||||
|
: undefined;
|
||||||
|
this.manufacturer = manufacturer;
|
||||||
|
this.model = model;
|
||||||
|
this.firmwareVersion = firmwareVersion;
|
||||||
|
this.serial = serial;
|
||||||
|
this.reconnectOnDisconnect = Boolean(reconnectOnDisconnect);
|
||||||
|
this.requiresPairing = Boolean(requiresPairing);
|
||||||
|
deepFreeze(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
merge = (partial) => new Core(Object.assign({}, this, partial));
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { Control } from "./control";
|
||||||
|
import { Sensors } from "./sensors";
|
||||||
|
import { Core } from "./core";
|
||||||
|
|
||||||
|
export { Control, Sensors, Core };
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import { deepFreeze, isFiniteNumber } from "../../utils";
|
||||||
|
import { validateCadenceQuality } from "../cadence";
|
||||||
|
|
||||||
|
export class Sensors {
|
||||||
|
constructor({
|
||||||
|
supportsHeartRate = false,
|
||||||
|
supportsCadence = false,
|
||||||
|
supportsPower = false,
|
||||||
|
hrSamplingHz,
|
||||||
|
cadenceSamplingHz,
|
||||||
|
powerSamplingHz,
|
||||||
|
cadenceSourceQuality,
|
||||||
|
} = {}) {
|
||||||
|
this.supportsHeartRate = Boolean(supportsHeartRate);
|
||||||
|
this.supportsCadence = Boolean(supportsCadence);
|
||||||
|
this.supportsPower = Boolean(supportsPower);
|
||||||
|
this.hrSamplingHz = isFiniteNumber(hrSamplingHz)
|
||||||
|
? Number(hrSamplingHz)
|
||||||
|
: undefined;
|
||||||
|
this.cadenceSamplingHz = isFiniteNumber(cadenceSamplingHz)
|
||||||
|
? Number(cadenceSamplingHz)
|
||||||
|
: undefined;
|
||||||
|
this.powerSamplingHz = isFiniteNumber(powerSamplingHz)
|
||||||
|
? Number(powerSamplingHz)
|
||||||
|
: undefined;
|
||||||
|
this.cadenceSourceQuality = validateCadenceQuality(cadenceSourceQuality);
|
||||||
|
deepFreeze(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
merge = (partial) => new Sensors(Object.assign({}, this, partial));
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
import { Control, Sensors, Core } from "./capabilities";
|
||||||
|
import { deepFreeze } from "../utils";
|
||||||
|
|
||||||
|
const DeviceType = Object.freeze({
|
||||||
|
HEART_RATE: "heart_rate",
|
||||||
|
CADENCE: "cadence",
|
||||||
|
TRAINER: "trainer",
|
||||||
|
UNKNOWN: "unknown",
|
||||||
|
});
|
||||||
|
|
||||||
|
export class Device {
|
||||||
|
constructor({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
core,
|
||||||
|
sensors,
|
||||||
|
control,
|
||||||
|
connected = false,
|
||||||
|
lastSeen,
|
||||||
|
} = {}) {
|
||||||
|
if (!id) throw new Error("Device requires id");
|
||||||
|
this.id = id;
|
||||||
|
this.name = name || id;
|
||||||
|
this.type = type;
|
||||||
|
this.core = core instanceof Core ? core : core ? new Core(core) : undefined;
|
||||||
|
this.sensors =
|
||||||
|
sensors instanceof Sensors
|
||||||
|
? sensors
|
||||||
|
: sensors
|
||||||
|
? new Sensors(sensors)
|
||||||
|
: undefined;
|
||||||
|
this.control =
|
||||||
|
control instanceof Control
|
||||||
|
? control
|
||||||
|
: control
|
||||||
|
? new Control(control)
|
||||||
|
: undefined;
|
||||||
|
this.connected = Boolean(connected);
|
||||||
|
this.lastSeen = lastSeen;
|
||||||
|
deepFreeze(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
withCore = (partial) =>
|
||||||
|
new Device(
|
||||||
|
Object.assign({}, this, {
|
||||||
|
core: this.core ? this.core.merge(partial) : new Core(partial),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
withSensors = (partial) =>
|
||||||
|
new Device(
|
||||||
|
Object.assign({}, this, {
|
||||||
|
sensors: this.sensors
|
||||||
|
? this.sensors.merge(partial)
|
||||||
|
: new Sensors(partial),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
withControl = (partial) =>
|
||||||
|
new Device(
|
||||||
|
Object.assign({}, this, {
|
||||||
|
control: this.control
|
||||||
|
? this.control.merge(partial)
|
||||||
|
: new Control(partial),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
const deepFreeze = (obj) => {
|
||||||
|
if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
|
||||||
|
Object.getOwnPropertyNames(obj).forEach((prop) => {
|
||||||
|
const value = obj[prop];
|
||||||
|
if (value && typeof value === "object") deepFreeze(value);
|
||||||
|
});
|
||||||
|
Object.freeze(obj);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFiniteNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
||||||
|
|
||||||
|
export { deepFreeze, isFiniteNumber };
|
||||||
Loading…
Reference in new issue