Add code for devices and sensors control

pull/1/head
lambda 5 months ago
parent 79d339a4d3
commit 2777cd69aa
Signed by: lambda
GPG Key ID: 07006F27729676AE

@ -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,17 @@
import { deepFreeze } from "../../utils";
export class CyclingPowerCapabilities {
constructor({ controlOpcodes = [], supportsCrankTorque = false } = {}) {
/*
* Если в конструктор передали ссылку на внешний массив,
* slice() возвращает новый массив. О бъект получает собственную копию,
* и дальнейшие изменения исходного массива снаружи не повлияют на
* this.controlOpcodes.
*/
this.controlOpcodes = Array.isArray(controlOpcodes)
? controlOpcodes.slice()
: [];
this.supportsCrankTorque = Boolean(supportsCrankTorque);
deepFreeze(this);
}
}

@ -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),
}),
);
}

@ -1,5 +1,4 @@
const startButton = document.getElementById("start");
const FTM_SERVICE_UUID = "00001825-0000-1000-8000-00805f9b34fb";
startButton.addEventListener("click", async () => {
try {

@ -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…
Cancel
Save