diff --git a/apps/btadv/app.ts b/apps/btadv/app.ts index 4ae75fae31..0c1803fdbd 100644 --- a/apps/btadv/app.ts +++ b/apps/btadv/app.ts @@ -720,7 +720,7 @@ const hook = (enable: boolean) => { // --- intervals --- const setIntervals = ( - locked: boolean = Bangle.isLocked(), + locked: ShortBoolean = Bangle.isLocked(), connected: boolean = NRF.getSecurityStatus().connected, ) => { changeInterval( diff --git a/apps/pace/ChangeLog b/apps/pace/ChangeLog new file mode 100644 index 0000000000..1a3bc17573 --- /dev/null +++ b/apps/pace/ChangeLog @@ -0,0 +1 @@ +0.01: New app! diff --git a/apps/pace/README.md b/apps/pace/README.md new file mode 100644 index 0000000000..7a1ea7c066 --- /dev/null +++ b/apps/pace/README.md @@ -0,0 +1,11 @@ +# Description + +A running pace app, useful for races. Will also record your splits and display them to you on the pause menu. + +Drag up/down on the pause menu to scroll through your splits. +Press the button to pause/resume - when resumed, pressing the button will pause instantly, regardless of whether the screen is locked. + +# Todo + +- Load splits on app start, button to reset (exs is always reset) +- Show total time on pause screen diff --git a/apps/pace/app-icon.js b/apps/pace/app-icon.js new file mode 100644 index 0000000000..9f7887cf4d --- /dev/null +++ b/apps/pace/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwIjgj//Ap8A8AFPgP4jAdEngFCv///AFC/8f+AFC/EBAodggF4FQgdDF4MeFoIhB8EPgEDApEHwAXDAosDApPwAoIdC//j4AFFGoV/AoJCDDogFIDoUAHYIFEKYxlFAAVAnwEDgF8JgaiBAoX8g6PDUIJGDVoJADg/8f74A==")) diff --git a/apps/pace/app.js b/apps/pace/app.js new file mode 100644 index 0000000000..c854df030e --- /dev/null +++ b/apps/pace/app.js @@ -0,0 +1,175 @@ +{ + var Layout_1 = require("Layout"); + var time_utils_1 = require("time_utils"); + var exs_1 = require("exstats").getStats(["dist", "pacec"], { + notify: { + dist: { + increment: 1000, + }, + }, + }); + var S_1 = require("Storage"); + var drawTimeout_1; + var splits_1 = []; + var splitOffset_1 = 0, splitOffsetPx_1 = 0; + var GPS_TIMEOUT_MS_1 = 30000; + var layout_1 = new Layout_1({ + type: "v", + c: [ + { + type: "txt", + font: "6x8:2", + label: "Pace", + id: "paceLabel", + pad: 4 + }, + { + type: "txt", + font: "Vector:40", + label: "", + id: "pace", + halign: 0 + }, + { + type: "txt", + font: "6x8:2", + label: "Time", + id: "timeLabel", + pad: 4 + }, + { + type: "txt", + font: "Vector:40", + label: "", + id: "time", + halign: 0 + }, + ] + }, { + lazy: true + }); + var draw_1 = function () { + if (!exs_1.state.active) { + drawSplits_1(); + return; + } + if (drawTimeout_1) + clearTimeout(drawTimeout_1); + drawTimeout_1 = setTimeout(draw_1, 1000); + var now = Date.now(); + var pace; + if ("time" in exs_1.state.thisGPS + && now - exs_1.state.thisGPS.time < GPS_TIMEOUT_MS_1) { + pace = exs_1.stats.pacec.getString(); + } + else { + pace = "No GPS"; + } + layout_1["time"].label = formatDuration_1(exs_1.state.duration); + layout_1["pace"].label = pace; + layout_1.render(); + }; + var pad2_1 = function (n) { return "0".concat(n).substr(-2); }; + var formatDuration_1 = function (ms) { + var tm = time_utils_1.decodeTime(ms); + if (tm.h) + return "".concat(tm.h, ":").concat(pad2_1(tm.m), ":").concat(pad2_1(tm.s)); + return "".concat(pad2_1(tm.m), ":").concat(pad2_1(tm.s)); + }; + var calculatePace_1 = function (split) { return formatDuration_1(split.time / split.dist * 1000); }; + var drawSplits_1 = function () { + g.clearRect(Bangle.appRect); + var barSize = 20; + var barSpacing = 10; + var w = g.getWidth(); + var h = g.getHeight(); + var max = splits_1.reduce(function (a, s) { return Math.max(a, s.time); }, 0); + g.setFont("6x8", 2).setFontAlign(-1, -1); + var i = 0; + for (;; i++) { + var split = splits_1[i + splitOffset_1]; + if (split == null) + break; + var y_1 = Bangle.appRect.y + i * (barSize + barSpacing) + barSpacing / 2; + if (y_1 > h) + break; + var size = w * split.time / max; + g.setColor("#00f").fillRect(0, y_1, size, y_1 + barSize); + var splitPace = calculatePace_1(split); + drawSplit_1(i, y_1, splitPace); + } + var pace = exs_1.stats.pacec.getString(); + var y = Bangle.appRect.y + i * (barSize + barSpacing) + barSpacing / 2; + drawSplit_1(i, y, pace); + }; + var drawSplit_1 = function (i, y, pace) { + g + .setColor(g.theme.fg) + .drawString("".concat(i + 1 + splitOffset_1, " ").concat(typeof pace === "number" ? pace.toFixed(2) : pace), 0, y); + }; + var pauseRun_1 = function () { + exs_1.stop(); + Bangle.setGPSPower(0, "pace"); + draw_1(); + }; + var resumeRun_1 = function () { + exs_1.resume(); + Bangle.setGPSPower(1, "pace"); + g.clearRect(Bangle.appRect); + layout_1.forgetLazyState(); + draw_1(); + }; + var onButton_1 = function () { + if (exs_1.state.active) + pauseRun_1(); + else + resumeRun_1(); + }; + exs_1.start(); + exs_1.stats.dist.on("notify", function (dist) { + var prev = { time: 0, dist: 0 }; + for (var _i = 0, splits_2 = splits_1; _i < splits_2.length; _i++) { + var s = splits_2[_i]; + prev.time += s.time; + prev.dist += s.dist; + } + var totalDist = dist.getValue(); + var thisSplit = totalDist - prev.dist; + var thisTime = exs_1.state.duration - prev.time; + while (thisSplit > 1000) { + splits_1.push({ dist: thisSplit, time: thisTime }); + thisTime = 0; + thisSplit -= 1000; + } + exs_1.state.notify.dist.next -= thisSplit; + S_1.writeJSON("pace.json", { splits: splits_1 }); + }); + Bangle.on('lock', function (locked) { + if (!locked && exs_1.state.active) + onButton_1(); + }); + setWatch(function () { return onButton_1(); }, BTN1, { repeat: true }); + Bangle.on('drag', function (e) { + if (exs_1.state.active || e.b === 0) + return; + splitOffsetPx_1 -= e.dy; + if (splitOffsetPx_1 > 20) { + if (splitOffset_1 < splits_1.length - 3) + splitOffset_1++, Bangle.buzz(30); + splitOffsetPx_1 = 0; + } + else if (splitOffsetPx_1 < -20) { + if (splitOffset_1 > 0) + splitOffset_1--, Bangle.buzz(30); + splitOffsetPx_1 = 0; + } + draw_1(); + }); + Bangle.on('twist', function () { + Bangle.setBacklight(1); + }); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + g.clearRect(Bangle.appRect); + draw_1(); +} diff --git a/apps/pace/app.png b/apps/pace/app.png new file mode 100644 index 0000000000..ff8f29cdca Binary files /dev/null and b/apps/pace/app.png differ diff --git a/apps/pace/app.ts b/apps/pace/app.ts new file mode 100644 index 0000000000..cf70fe4061 --- /dev/null +++ b/apps/pace/app.ts @@ -0,0 +1,225 @@ +{ +const Layout = require("Layout"); +const time_utils = require("time_utils"); +const exs = require("exstats").getStats( + ["dist", "pacec"], + { + notify: { + dist: { + increment: 1000, + }, + }, + }, +); +const S = require("Storage"); + +let drawTimeout: TimeoutId | undefined; + +type Dist = number & { brand: 'dist' }; +type Time = number & { brand: 'time' }; + +type Split = { + dist: Dist, + time: Time, +}; + +const splits: Split[] = []; // times +let splitOffset = 0, splitOffsetPx = 0; + +const GPS_TIMEOUT_MS = 30000; + +const layout = new Layout({ + type: "v", + c: [ + { + type: "txt", + font: "6x8:2", + label: "Pace", + id: "paceLabel", + pad: 4 + }, + { + type: "txt", + font: "Vector:40", + label: "", + id: "pace", + halign: 0 + }, + { + type: "txt", + font: "6x8:2", + label: "Time", + id: "timeLabel", + pad: 4 + }, + { + type: "txt", + font: "Vector:40", + label: "", + id: "time", + halign: 0 + }, + ] +}, { + lazy: true +}); + +const draw = () => { + if (!exs.state.active) { + // no draw-timeout here, only on user interaction + drawSplits(); + return; + } + + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(draw, 1000); + + const now = Date.now(); + + let pace: string; + if ("time" in exs.state.thisGPS + && now - (exs.state.thisGPS.time as unknown as number) < GPS_TIMEOUT_MS) + { + pace = exs.stats.pacec.getString() + }else{ + pace = "No GPS"; + } + + layout["time"]!.label = formatDuration(exs.state.duration); + layout["pace"]!.label = pace; + layout.render(); +}; + +const pad2 = (n: number) => `0${n}`.substr(-2); + +const formatDuration = (ms: number) => { + const tm = time_utils.decodeTime(ms); + if(tm.h) + return `${tm.h}:${pad2(tm.m)}:${pad2(tm.s)}`; + return `${pad2(tm.m)}:${pad2(tm.s)}`; +}; + +// divide by actual distance, scale to milliseconds +const calculatePace = (split: Split) => formatDuration(split.time / split.dist * 1000); + +const drawSplits = () => { + g.clearRect(Bangle.appRect); + + const barSize = 20; + const barSpacing = 10; + const w = g.getWidth(); + const h = g.getHeight(); + + const max = splits.reduce((a, s) => Math.max(a, s.time), 0); + + g.setFont("6x8", 2).setFontAlign(-1, -1); + + let i = 0; + for(; ; i++) { + const split = splits[i + splitOffset]; + if (split == null) break; + + const y = Bangle.appRect.y + i * (barSize + barSpacing) + barSpacing / 2; + if (y > h) break; + + const size = w * split.time / max; // Scale bar height based on pace + g.setColor("#00f").fillRect(0, y, size, y + barSize); + + const splitPace = calculatePace(split); // Pace per km + drawSplit(i, y, splitPace); + } + + const pace = exs.stats.pacec.getString(); + + const y = Bangle.appRect.y + i * (barSize + barSpacing) + barSpacing / 2; + drawSplit(i, y, pace); +}; + +const drawSplit = (i: number, y: number, pace: number | string) => { + g + .setColor(g.theme.fg) + .drawString( + `${i + 1 + splitOffset} ${typeof pace === "number" ? pace.toFixed(2) : pace}`, + 0, + y + ); +}; + +const pauseRun = () => { + exs.stop(); + Bangle.setGPSPower(0, "pace") + draw(); +}; + +const resumeRun = () => { + exs.resume(); + Bangle.setGPSPower(1, "pace"); + + g.clearRect(Bangle.appRect); // splits -> layout, clear. layout -> splits, fine + layout.forgetLazyState(); + draw(); +}; + +const onButton = () => { + if (exs.state.active) + pauseRun(); + else + resumeRun(); +}; + +exs.start(); // aka reset + +exs.stats.dist.on("notify", (dist) => { + const prev = { time: 0, dist: 0 }; + for(const s of splits){ + prev.time += s.time; + prev.dist += s.dist; + } + + const totalDist = dist.getValue(); + let thisSplit = totalDist - prev.dist; + let thisTime = exs.state.duration - prev.time; + + while(thisSplit > 1000) { + splits.push({ dist: thisSplit as Dist, time: thisTime as Time }); + thisTime = 0; // if we've jumped more than 1k, credit the time to the first split + thisSplit -= 1000; + } + + // subtract off the next split notify + exs.state.notify.dist.next -= thisSplit; + + S.writeJSON("pace.json", { splits }); +}); + +Bangle.on('lock', locked => { + // treat an unlock (while running) as a pause + if(!locked && exs.state.active) onButton(); +}); + +setWatch(() => onButton(), BTN1, { repeat: true }); + +Bangle.on('drag', e => { + if (exs.state.active || e.b === 0) return; + + splitOffsetPx -= e.dy; + if (splitOffsetPx > 20) { + if (splitOffset < splits.length-3) splitOffset++, Bangle.buzz(30); + splitOffsetPx = 0; + } else if (splitOffsetPx < -20) { + if (splitOffset > 0) splitOffset--, Bangle.buzz(30); + splitOffsetPx = 0; + } + draw(); +}); + +Bangle.on('twist', () => { + Bangle.setBacklight(1); +}); + +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +g.clearRect(Bangle.appRect); +draw(); +} diff --git a/apps/pace/metadata.json b/apps/pace/metadata.json new file mode 100644 index 0000000000..e7066c9586 --- /dev/null +++ b/apps/pace/metadata.json @@ -0,0 +1,14 @@ +{ + "id": "pace", + "name": "Pace", + "version": "0.01", + "description": "Show pace and time running splits", + "icon": "app.png", + "tags": "run,running,fitness,outdoors", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + { "name": "pace.app.js","url": "app.js" }, + { "name": "pace.img","url": "app-icon.js","evaluate": true } + ] +} diff --git a/modules/exstats.js b/modules/exstats.js index 5cd0374a7a..6cb6a8773b 100644 --- a/modules/exstats.js +++ b/modules/exstats.js @@ -158,8 +158,8 @@ Bangle.on("GPS", function(fix) { if (stats["pacea"]) stats["pacea"].emit("changed",stats["pacea"]); if (stats["pacec"]) stats["pacec"].emit("changed",stats["pacec"]); if (state.notify.dist.increment > 0 && state.notify.dist.next <= state.distance) { - stats["dist"].emit("notify",stats["dist"]); state.notify.dist.next = state.notify.dist.next + state.notify.dist.increment; + stats["dist"].emit("notify",stats["dist"]); } }); @@ -169,8 +169,8 @@ Bangle.on("step", function(steps) { state.stepHistory[0] += steps-state.lastSteps; state.lastSteps = steps; if (state.notify.step.increment > 0 && state.notify.step.next <= steps) { - stats["step"].emit("notify",stats["step"]); state.notify.step.next = state.notify.step.next + state.notify.step.increment; + stats["step"].emit("notify",stats["step"]); } }); Bangle.on("HRM", function(h) { @@ -336,8 +336,8 @@ exports.getStats = function(statIDs, options) { if (stats["bpm"]) stats["bpm"].emit("changed",stats["bpm"]); } if (state.notify.time.increment > 0 && state.notify.time.next <= now) { - stats["time"].emit("notify",stats["time"]); state.notify.time.next = state.notify.time.next + state.notify.time.increment; + stats["time"].emit("notify",stats["time"]); } }, 1000); function reset() { diff --git a/typescript/types/exstats.d.ts b/typescript/types/exstats.d.ts new file mode 100644 index 0000000000..7e980882dd --- /dev/null +++ b/typescript/types/exstats.d.ts @@ -0,0 +1,83 @@ +declare module ExStats { + type StatsId = "time" | "dist" | "step" | "bpm" | "maxbpm" | "pacea" | "pacec" | "speed" | "caden" | "altg" | "altb"; + + function getList(): { name: string, id: StatsId }[]; + + function getStats( + ids: Ids[], + options?: Options + ): StatsInst; + + type Options = { + paceLength?: number, + notify?: NotifyInput, + }; + + type Notify = { + [key in Ids & ("dist" | "step" | "time")]: { + increment: number, + next: number, + } + }; + + type NotifyInput = { + [K in keyof Notify]?: + Omit< + Notify[K], "next" + > & { + next?: number, + }; + }; + + type StatsInst = { + stats: Stats, + state: State, + start(): void, + stop(): void, + resume(): void, + }; + + type State = { + notify: Notify, + + active: boolean, + duration: number, + startTime: number, + lastTime: number, + + BPM: number, + BPMage: number, + maxBPM: number, + + alt: number | undefined, + alti: number, + + avrSpeed: number, + curSpeed: number, + distance: number, + + startSteps: number, + lastSteps: number, + stepHistory: Uint8Array, + stepsPerMin: number, + + thisGPS: GPSFix | {}, + lastGPS: GPSFix | {}, + }; + + type Stats = { + [key in Ids]: Stat + }; + + type Stat = { + title: string, + getValue(): number, + getString(): string, + id: StatsId, + + on(what: "changed", cb: (stat: Stat) => void): void; + + // emitted by dist|step|time + on(what: "notify", cb: (stat: Stat) => void): void; + }; +} diff --git a/typescript/types/layout.d.ts b/typescript/types/layout.d.ts index 55ddd7135a..4329d08313 100644 --- a/typescript/types/layout.d.ts +++ b/typescript/types/layout.d.ts @@ -7,7 +7,16 @@ type ExtractIds = [Depth] extends [never] ? never : (T extends { id?: infer Id extends string } - ? { [k in Id]: { -readonly [P in keyof T]: T[P] extends string ? string : T[P] } } + ? { + [k in Id]: { + -readonly [P in keyof T]: + T[P] extends string + ? string + : T[P] extends number + ? number | undefined + : T[P] + } + } : never) | ( diff --git a/typescript/types/main.d.ts b/typescript/types/main.d.ts index 24633db5ba..e00577afbf 100644 --- a/typescript/types/main.d.ts +++ b/typescript/types/main.d.ts @@ -586,7 +586,7 @@ declare class ESP32 { * @param {boolean} enable - switches Bluetooth on or off * @url http://www.espruino.com/Reference#l_ESP32_enableBLE */ - static enableBLE(enable: boolean): void; + static enableBLE(enable: ShortBoolean): void; /** * Switches Wifi off/on, removes saved code from Flash, resets the board, and on @@ -595,7 +595,7 @@ declare class ESP32 { * @param {boolean} enable - switches Wifi on or off * @url http://www.espruino.com/Reference#l_ESP32_enableWifi */ - static enableWifi(enable: boolean): void; + static enableWifi(enable: ShortBoolean): void; /** * This function is useful for ESP32 [OTA Updates](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ota.html) @@ -609,7 +609,7 @@ declare class ESP32 { * @param {boolean} isValid - Set whether this app is valid or not. If `isValid==false` the device will reboot. * @url http://www.espruino.com/Reference#l_ESP32_setOTAValid */ - static setOTAValid(isValid: boolean): void; + static setOTAValid(isValid: ShortBoolean): void; } @@ -934,11 +934,11 @@ declare class NRF { /** * Called when Bluetooth advertising starts or stops on Espruino * @param {string} event - The event to listen to. - * @param {(isAdvertising: boolean) => void} callback - A function that is executed when the event occurs. Its arguments are: + * @param {(isAdvertising: ShortBoolean) => void} callback - A function that is executed when the event occurs. Its arguments are: * * `isAdvertising` Whether we are advertising or not * @url http://www.espruino.com/Reference#l_NRF_advertising */ - static on(event: "advertising", callback: (isAdvertising: boolean) => void): void; + static on(event: "advertising", callback: (isAdvertising: ShortBoolean) => void): void; /** * Called during the bonding process to update on status @@ -1064,7 +1064,7 @@ declare class NRF { * @returns {any} MAC address - a string of the form 'aa:bb:cc:dd:ee:ff' * @url http://www.espruino.com/Reference#l_NRF_getAddress */ - static getAddress(current: boolean): any; + static getAddress(current: ShortBoolean): any; /** * Set this device's default Bluetooth MAC address: @@ -1643,7 +1643,7 @@ declare class NRF { * @param {boolean} lowPower - Whether the connection is low power or not * @url http://www.espruino.com/Reference#l_NRF_setLowPowerConnection */ - static setLowPowerConnection(lowPower: boolean): void; + static setLowPowerConnection(lowPower: ShortBoolean): void; /** * Enables NFC and starts advertising the given URL. For example: @@ -1770,7 +1770,7 @@ declare class NRF { * @param {boolean} positive - `true` for positive action, `false` for negative * @url http://www.espruino.com/Reference#l_NRF_ancsAction */ - static ancsAction(uid: number, positive: boolean): void; + static ancsAction(uid: number, positive: ShortBoolean): void; /** * Get ANCS info for a notification event received via `E.ANCS`, e.g.: @@ -2059,7 +2059,7 @@ declare class NRF { * @param {boolean} whitelisting - Are we using a whitelist? (default false) * @url http://www.espruino.com/Reference#l_NRF_setWhitelist */ - static setWhitelist(whitelisting: boolean): void; + static setWhitelist(whitelisting: ShortBoolean): void; /** * When connected, Bluetooth LE devices communicate at a set interval. Lowering the @@ -2245,7 +2245,7 @@ declare class NRF { * @returns {any} A promise * @url http://www.espruino.com/Reference#l_NRF_startBonding */ - static startBonding(forceRepair: boolean): any; + static startBonding(forceRepair: ShortBoolean): any; } @@ -2350,7 +2350,7 @@ declare class Pixl { * @param {boolean} isOn - True if the LCD should be on, false if not * @url http://www.espruino.com/Reference#l_Pixl_setLCDPower */ - static setLCDPower(isOn: boolean): void; + static setLCDPower(isOn: ShortBoolean): void; /** * Writes a command directly to the ST7567 LCD controller @@ -2480,7 +2480,7 @@ declare class url { * @returns {any} An object containing options for ```http.request``` or ```http.get```. Contains `method`, `host`, `path`, `pathname`, `search`, `port` and `query` * @url http://www.espruino.com/Reference#l_url_parse */ - static parse(urlStr: any, parseQuery: boolean): any; + static parse(urlStr: any, parseQuery: ShortBoolean): any; } @@ -3730,7 +3730,7 @@ declare class Qwiic { * @returns {any} The same Qwiic object (for call chaining) * @url http://www.espruino.com/Reference#l_Qwiic_setPower */ - setPower(isOn: boolean): any; + setPower(isOn: ShortBoolean): any; /** * @returns {any} An I2C object using this Qwiic connector, already set up @@ -3800,11 +3800,11 @@ declare class Bangle { /** * Has the watch been moved so that it is face-up, or not face up? * @param {string} event - The event to listen to. - * @param {(up: boolean) => void} callback - A function that is executed when the event occurs. Its arguments are: + * @param {(up: ShortBoolean) => void} callback - A function that is executed when the event occurs. Its arguments are: * * `up` `true` if face-up * @url http://www.espruino.com/Reference#l_Bangle_faceUp */ - static on(event: "faceUp", callback: (up: boolean) => void): void; + static on(event: "faceUp", callback: (up: ShortBoolean) => void): void; /** * This event happens when the watch has been twisted around it's axis - for @@ -3819,11 +3819,11 @@ declare class Bangle { /** * Is the battery charging or not? * @param {string} event - The event to listen to. - * @param {(charging: boolean) => void} callback - A function that is executed when the event occurs. Its arguments are: + * @param {(charging: ShortBoolean) => void} callback - A function that is executed when the event occurs. Its arguments are: * * `charging` `true` if charging * @url http://www.espruino.com/Reference#l_Bangle_charging */ - static on(event: "charging", callback: (charging: boolean) => void): void; + static on(event: "charging", callback: (charging: ShortBoolean) => void): void; /** * Magnetometer/Compass data available with `{x,y,z,dx,dy,dz,heading}` object as a @@ -3848,7 +3848,7 @@ declare class Bangle { * Raw NMEA GPS / u-blox data messages received as a string * To get this event you must turn the GPS on with `Bangle.setGPSPower(1)`. * @param {string} event - The event to listen to. - * @param {(nmea: any, dataLoss: boolean) => void} callback - A function that is executed when the event occurs. Its arguments are: + * @param {(nmea: any, dataLoss: ShortBoolean) => void} callback - A function that is executed when the event occurs. Its arguments are: * * `nmea` A string containing the raw NMEA data from the GPS * * `dataLoss` This is set to true if some lines of GPS data have previously been lost (eg because system was too busy to queue up a GPS-raw event) * @url http://www.espruino.com/Reference#l_Bangle_GPS-raw @@ -3942,31 +3942,31 @@ declare class Bangle { * Has the screen been turned on or off? Can be used to stop tasks that are no * longer useful if nothing is displayed. Also see `Bangle.isLCDOn()` * @param {string} event - The event to listen to. - * @param {(on: boolean) => void} callback - A function that is executed when the event occurs. Its arguments are: + * @param {(on: ShortBoolean) => void} callback - A function that is executed when the event occurs. Its arguments are: * * `on` `true` if screen is on * @url http://www.espruino.com/Reference#l_Bangle_lcdPower */ - static on(event: "lcdPower", callback: (on: boolean) => void): void; + static on(event: "lcdPower", callback: (on: ShortBoolean) => void): void; /** * Has the backlight been turned on or off? Can be used to stop tasks that are no * longer useful if want to see in sun screen only. Also see `Bangle.isBacklightOn()` * @param {string} event - The event to listen to. - * @param {(on: boolean) => void} callback - A function that is executed when the event occurs. Its arguments are: + * @param {(on: ShortBoolean) => void} callback - A function that is executed when the event occurs. Its arguments are: * * `on` `true` if backlight is on * @url http://www.espruino.com/Reference#l_Bangle_backlight */ - static on(event: "backlight", callback: (on: boolean) => void): void; + static on(event: "backlight", callback: (on: ShortBoolean) => void): void; /** * Has the screen been locked? Also see `Bangle.isLocked()` * @param {string} event - The event to listen to. - * @param {(on: boolean, reason: string) => void} callback - A function that is executed when the event occurs. Its arguments are: + * @param {(on: ShortBoolean, reason: string) => void} callback - A function that is executed when the event occurs. Its arguments are: * * `on` `true` if screen is locked, `false` if it is unlocked and touchscreen/buttons will work * * `reason` (2v20 onwards) If known, the reason for locking/unlocking - 'button','js','tap','doubleTap','faceUp','twist','timeout' * @url http://www.espruino.com/Reference#l_Bangle_lock */ - static on(event: "lock", callback: (on: boolean, reason: string) => void): void; + static on(event: "lock", callback: (on: ShortBoolean, reason: string) => void): void; /** * If the watch is tapped, this event contains information on the way it was @@ -4103,7 +4103,7 @@ declare class Bangle { * @param {boolean} isOn - True if the LCD backlight should be on, false if not * @url http://www.espruino.com/Reference#l_Bangle_setBacklight */ - static setBacklight(isOn: boolean): void; + static setBacklight(isOn: ShortBoolean): void; /** * This function can be used to turn Bangle.js's LCD off or on. @@ -4122,7 +4122,7 @@ declare class Bangle { * @param {boolean} isOn - True if the LCD should be on, false if not * @url http://www.espruino.com/Reference#l_Bangle_setLCDPower */ - static setLCDPower(isOn: boolean): void; + static setLCDPower(isOn: ShortBoolean): void; /** * This function can be used to adjust the brightness of Bangle.js's display, and @@ -4309,6 +4309,9 @@ declare class Bangle { * off * * `btnLoadTimeout` how many milliseconds does the home button have to be pressed * for before the clock is reloaded? 1500ms default, or 0 means never. + * * `manualWatchdog` if set, this disables automatic kicking of the watchdog timer + * from the interrupt (when the button isn't held). You will then have to manually + * call `E.kickWatchdog()` from your code or the watch will reset after ~5 seconds. * * `hrmPollInterval` set the requested poll interval (in milliseconds) for the * heart rate monitor. On Bangle.js 2 only 10,20,40,80,160,200 ms are supported, * and polling rate may not be exact. The algorithm's filtering is tuned for @@ -4362,7 +4365,7 @@ declare class Bangle { * @param {boolean} isLocked - `true` if the Bangle is locked (no user input allowed) * @url http://www.espruino.com/Reference#l_Bangle_setLocked */ - static setLocked(isLocked: boolean): void; + static setLocked(isLocked: ShortBoolean): void; /** * Also see the `Bangle.lock` event @@ -4925,7 +4928,7 @@ declare class Bangle { * @param {boolean} noReboot - Do not reboot the watch when done (default false, so will reboot) * @url http://www.espruino.com/Reference#l_Bangle_factoryReset */ - static factoryReset(noReboot: boolean): void; + static factoryReset(noReboot: ShortBoolean): void; /** * Returns the rectangle on the screen that is currently reserved for the app. @@ -5170,7 +5173,7 @@ declare class BluetoothRemoteGATTServer { * @returns {any} A `Promise` that is resolved (or rejected) when the bonding is complete * @url http://www.espruino.com/Reference#l_BluetoothRemoteGATTServer_startBonding */ - startBonding(forceRePair: boolean): Promise; + startBonding(forceRePair: ShortBoolean): Promise; /** * Return an object with information about the security state of the current @@ -5453,9 +5456,10 @@ declare class Graphics { * An object of other options. `{ zigzag : true/false(default), vertical_byte : true/false(default), msb : true/false(default), color_order: 'rgb'(default),'bgr',etc }` * `zigzag` = whether to alternate the direction of scanlines for rows * `vertical_byte` = whether to align bits in a byte vertically or not - * `msb` = when bits<8, store pixels most significant bit first, when bits>8, store most significant byte first + * `msb` = when bits<8, store pixels most significant bit first, when bits>8, store most significant byte first (as of 2v25, msb:true is default) * `interleavex` = Pixels 0,2,4,etc are from the top half of the image, 1,3,5,etc from the bottom half. Used for P3 LED panels. * `color_order` = re-orders the colour values that are supplied via setColor + * `buffer` = if specified, createArrayBuffer won't create a new buffer but will use the given one * @returns {any} The new Graphics object * @url http://www.espruino.com/Reference#l_Graphics_createArrayBuffer */ @@ -5586,7 +5590,7 @@ declare class Graphics { * @param {boolean} [all] - [optional] (only on some devices) If `true` then copy all pixels, not just those that have changed. * @url http://www.espruino.com/Reference#l_Graphics_flip */ - flip(all?: boolean): void; + flip(all?: ShortBoolean): void; /** * On Graphics instances with an offscreen buffer, this is an `ArrayBuffer` that @@ -5650,7 +5654,7 @@ declare class Graphics { * @returns {any} The instance of Graphics this was called on, to allow call chaining * @url http://www.espruino.com/Reference#l_Graphics_clear */ - clear(reset?: boolean): Graphics; + clear(reset?: ShortBoolean): Graphics; /** * Fill a rectangular area in the Foreground Color @@ -6584,7 +6588,7 @@ declare class WioLTE { * @param {boolean} onoff - Whether to turn the Grove connectors power on or off (D38/D39 are always powered) * @url http://www.espruino.com/Reference#l_WioLTE_setGrovePower */ - static setGrovePower(onoff: boolean): void; + static setGrovePower(onoff: ShortBoolean): void; /** * Turn power to the WIO's LED on or off. @@ -6594,7 +6598,7 @@ declare class WioLTE { * @param {boolean} onoff - true = on, false = off * @url http://www.espruino.com/Reference#l_WioLTE_setLEDPower */ - static setLEDPower(onoff: boolean): void; + static setLEDPower(onoff: ShortBoolean): void; /** * @returns {any} @@ -6641,33 +6645,83 @@ declare class WioLTE { * @url http://www.espruino.com/Reference#Waveform */ declare class Waveform { + /** + * Event emitted when playback has finished + * @param {string} event - The event to listen to. + * @param {(buffer: any) => void} callback - A function that is executed when the event occurs. Its arguments are: + * * `buffer` the last played buffer + * @url http://www.espruino.com/Reference#l_Waveform_finish + */ + static on(event: "finish", callback: (buffer: any) => void): void; + + /** + * When in double-buffered mode, this event is emitted when the `Waveform` class swaps to playing a new buffer - so you should then fill this current buffer up with new data. + * @param {string} event - The event to listen to. + * @param {(buffer: any) => void} callback - A function that is executed when the event occurs. Its arguments are: + * * `buffer` the last played buffer (which now needs to be filled ready for playback) + * @url http://www.espruino.com/Reference#l_Waveform_buffer + */ + static on(event: "buffer", callback: (buffer: any) => void): void; + /** * Create a waveform class. This allows high speed input and output of waveforms. * It has an internal variable called `buffer` (as well as `buffer2` when * double-buffered - see `options` below) which contains the data to input/output. + * Options can contain: + * ```JS + * { + * doubleBuffer : bool // whether to allocate two buffers or not (default false) + * bits : 8/16 // the amount of bits to use (default 8). + * } + * ``` * When double-buffered, a 'buffer' event will be emitted each time a buffer is * finished with (the argument is that buffer). When the recording stops, a * 'finish' event will be emitted (with the first argument as the buffer). + * ```JS + * // Output a sine wave + * var w = new Waveform(1000); + * for (var i=0;i<1000;i++) w.buffer[i]=128+120*Math.sin(i/2); + * analogWrite(H0, 0.5, {freq:80000}); // set up H0 to output an analog value by PWM + * w.on("finish", () => print("Done!")) + * w.startOutput(H0,8000); // start playback + * // On 2v25, from Storage + * var f = require("Storage").read("sound.pcm"); + * var w = new Waveform(E.toArrayBuffer(f)); + * w.on("finish", () => print("Done!")) + * w.startOutput(H0,8000); // start playback + * ``` + * See https://www.espruino.com/Waveform for more examples. * @constructor * - * @param {number} samples - The number of samples - * @param {any} options - Optional options struct `{doubleBuffer:bool, bits : 8/16}` where: `doubleBuffer` is whether to allocate two buffers or not (default false), and bits is the amount of bits to use (default 8). + * @param {any} samples - The number of samples to allocate as an integer, *or* an arraybuffer (2v25+) containing the samples + * @param {any} [options] - [optional] options struct `{ doubleBuffer:bool, bits : 8/16 }` (see below) * @returns {any} An Waveform object * @url http://www.espruino.com/Reference#l_Waveform_Waveform */ - static new(samples: number, options: any): any; + static new(samples: any, options?: any): any; /** * Will start outputting the waveform on the given pin - the pin must have * previously been initialised with analogWrite. If not repeating, it'll emit a * `finish` event when it is done. + * ``` + * { + * time : float, // the that the waveform with start output at, e.g. `getTime()+1` (otherwise it is immediate) + * repeat : bool, // whether to repeat the given sample + * npin : Pin, // If specified, the waveform is output across two pins (see below) + * } + * ``` + * Using `npin` allows you to split the Waveform output between two pins and hence avoid + * any DC bias (or need to capacitor), for instance you could attach a speaker to `H0` and + * `H1` on Jolt.js. When the value in the waveform was at 50% both outputs would be 0, + * below 50% the signal would be on `npin` with `pin` as 0, and above 50% it would be on `pin` with `npin` as 0. * * @param {Pin} output - The pin to output on * @param {number} freq - The frequency to output each sample at - * @param {any} options - Optional options struct `{time:float,repeat:bool}` where: `time` is the that the waveform with start output at, e.g. `getTime()+1` (otherwise it is immediate), `repeat` is a boolean specifying whether to repeat the give sample + * @param {any} [options] - [optional] options struct `{time:float, repeat:bool, npin:Pin}` (see below) * @url http://www.espruino.com/Reference#l_Waveform_startOutput */ - startOutput(output: Pin, freq: number, options: any): void; + startOutput(output: Pin, freq: number, options?: any): void; /** * Will start inputting the waveform on the given pin that supports analog. If not @@ -6675,10 +6729,10 @@ declare class Waveform { * * @param {Pin} output - The pin to output on * @param {number} freq - The frequency to output each sample at - * @param {any} options - Optional options struct `{time:float,repeat:bool}` where: `time` is the that the waveform with start output at, e.g. `getTime()+1` (otherwise it is immediate), `repeat` is a boolean specifying whether to repeat the give sample + * @param {any} [options] - [optional] options struct `{time:float,repeat:bool}` where: `time` is the that the waveform with start output at, e.g. `getTime()+1` (otherwise it is immediate), `repeat` is a boolean specifying whether to repeat the give sample * @url http://www.espruino.com/Reference#l_Waveform_startInput */ - startInput(output: Pin, freq: number, options: any): void; + startInput(output: Pin, freq: number, options?: any): void; /** * Stop a waveform that is currently outputting @@ -7869,11 +7923,24 @@ interface MathConstructor { pow(x: number, y: number): number; /** - * @returns {number} A random number between 0 and 1 + * @returns {number} A random number X, where `0 <= X < 1` * @url http://www.espruino.com/Reference#l_Math_random */ random(): number; + /** + * (Added in 2v25) Returns a random integer `X`, where `0 <= X < range`, or `-2147483648 <= X <= 2147483647` if `range <= 0` or `undefined` + * If `range` is supplied, this value is created using `modulo` of a 31 bit integer, so as `val` gets larger (24+ bits) + * the values produced will be less randomly distributed, and no values above `0x7FFFFFFF` will ever be returned. + * If `val==undefined` or `val<=0` a **32 bit** random number will be returned as an int (`-2147483648` .. `2147483647`). + * **Note:** this is not part of the JS spec, but is included in Espruino as it makes a lot of sense on embedded targets + * + * @param {number} range - How big a random number do we want + * @returns {number} A random integer + * @url http://www.espruino.com/Reference#l_Math_randInt + */ + randInt(range: number): number; + /** * * @param {number} x - The value to round @@ -8294,6 +8361,10 @@ declare class E { * menu is removed * * (Bangle.js 2) `scroll : int` - an integer specifying how much the initial * menu should be scrolled by + * * (Bangle.js 2) The mapped functions can consider the touch event that interacted with the entry: + * `"Entry" : function(touch) { ... }` + * * This is also true of `onchange` mapped functions in entry objects: + * `onchange : (value, touch) => { ... }` * * The object returned by `E.showMenu` contains: * * (Bangle.js 2) `scroller` - the object returned by `E.showScroller` - * `scroller.scroll` returns the amount the menu is currently scrolled by @@ -8397,6 +8468,7 @@ declare class E { * draw : function(idx, rect) { ... } * // a function to call when the item is selected, touch parameter is only relevant * // for Bangle.js 2 and contains the coordinates touched inside the selected item + * // as well as the type of the touch - see `Bangle.touch`. * select : function(idx, touch) { ... } * // optional function to be called when 'back' is tapped * back : function() { ...} @@ -9047,6 +9119,23 @@ declare class E { */ static setClock(options: number | { M: number, N: number, P: number, Q: number, latency?: number, PCLK?: number, PCLK2?: number }): number; + /** + * On boards other than STM32 this currently just returns `undefined` + * ### STM32 + * See `E.setClock` for more information. + * Returns: + * ``` + * { + * sysclk, hclk, pclk1, pclk2, // various clocks in Hz + * M, N, P, Q, PCLK1, PCLK2 // STM32F4: currently set divisors + * RTCCLKSource : "LSI/LSE/HSE_Div#" // STM32F4 source for RTC clock + * } + * ``` + * @returns {any} An object containing information about the current clock + * @url http://www.espruino.com/Reference#l_E_getClock + */ + static getClock(): any; + /** * Changes the device that the JS console (otherwise known as the REPL) is attached * to. If the console is on a device, that device can be used for programming @@ -9186,7 +9275,7 @@ declare class E { * @returns {number} The address of the given variable * @url http://www.espruino.com/Reference#l_E_getAddressOf */ - static getAddressOf(v: any, flatAddress: boolean): number; + static getAddressOf(v: any, flatAddress: ShortBoolean): number; /** * Take each element of the `from` array, look it up in `map` (or call @@ -9519,7 +9608,7 @@ declare class E { * @returns {number} The RTC prescaler's current value * @url http://www.espruino.com/Reference#l_E_getRTCPrescaler */ - static getRTCPrescaler(calibrate: boolean): number; + static getRTCPrescaler(calibrate: ShortBoolean): number; /** * This function returns an object detailing the current **estimated** power usage @@ -9646,7 +9735,7 @@ declare class OneWire { * @param {boolean} power - Whether to leave power on after write (default is false) * @url http://www.espruino.com/Reference#l_OneWire_write */ - write(data: any, power: boolean): void; + write(data: any, power: ShortBoolean): void; /** * Read a byte @@ -10140,6 +10229,8 @@ interface Array { /** * Return an array which is made from the following: ```A.map(function) = * [function(A[0]), function(A[1]), ...]``` + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.map(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} function - Function used to map one item to another * @param {any} [thisArg] - [optional] If specified, the function is called with 'this' set to thisArg @@ -10150,6 +10241,8 @@ interface Array { /** * Executes a provided function once per array element. + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.forEach(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} function - Function to be executed * @param {any} [thisArg] - [optional] If specified, the function is called with 'this' set to thisArg @@ -10160,6 +10253,8 @@ interface Array { /** * Return an array which contains only those elements for which the callback * function returns 'true' + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.filter(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} function - Function to be executed * @param {any} [thisArg] - [optional] If specified, the function is called with 'this' set to thisArg @@ -10176,6 +10271,8 @@ interface Array { * ["Hello","There","World"].find(a=>a[0]=="T") * // returns "There" * ``` + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.find(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} function - Function to be executed * @returns {any} The array element where `function` returns `true`, or `undefined` @@ -10191,6 +10288,8 @@ interface Array { * ["Hello","There","World"].findIndex(a=>a[0]=="T") * // returns 1 * ``` + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.findIndex(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} function - Function to be executed * @returns {any} The array element's index where `function` returns `true`, or `-1` @@ -10201,6 +10300,8 @@ interface Array { /** * Return 'true' if the callback returns 'true' for any of the elements in the * array + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.some(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} function - Function to be executed * @param {any} [thisArg] - [optional] If specified, the function is called with 'this' set to thisArg @@ -10211,6 +10312,8 @@ interface Array { /** * Return 'true' if the callback returns 'true' for every element in the array + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.every(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} function - Function to be executed * @param {any} [thisArg] - [optional] If specified, the function is called with 'this' set to thisArg @@ -10223,13 +10326,15 @@ interface Array { * Execute `previousValue=initialValue` and then `previousValue = * callback(previousValue, currentValue, index, array)` for each element in the * array, and finally return previousValue. + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.reduce(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} callback - Function used to reduce the array * @param {any} initialValue - if specified, the initial value to pass to the function * @returns {any} The value returned by the last function called * @url http://www.espruino.com/Reference#l_Array_reduce */ - reduce(callback: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue?: T): T; + reduce(callback: (previousValue: O, currentValue: T, currentIndex: number, array: T[]) => O, initialValue?: O): O; /** * Both remove and add items to an array @@ -10274,6 +10379,8 @@ interface Array { /** * Do an in-place quicksort of the array + * **Note:** Do not modify the array you're iterating over from inside the callback (`a.sort(()=>a.push(0))`). + * It will cause non-spec-compliant behaviour. * * @param {any} var - A function to use to compare array elements (or undefined) * @returns {any} This array object @@ -10339,8 +10446,6 @@ interface JSONConstructor { /** * Parse the given JSON string into a JavaScript object - * NOTE: This implementation uses eval() internally, and as such it is unsafe as it - * can allow arbitrary JS commands to be executed. * * @param {any} string - A JSON string * @returns {any} The JavaScript object created by parsing the data string @@ -10922,7 +11027,7 @@ declare class Serial { * @param {boolean} force - Whether to force the console to this port * @url http://www.espruino.com/Reference#l_Serial_setConsole */ - setConsole(force: boolean): void; + setConsole(force: ShortBoolean): void; /** * Setup this Serial port with the given baud rate and options. @@ -11595,7 +11700,7 @@ declare class Pin { * @param {boolean} value - Whether to set output high (true/1) or low (false/0) * @url http://www.espruino.com/Reference#l_Pin_write */ - write(value: boolean): void; + write(value: ShortBoolean): void; /** * Sets the output state of the pin to the parameter given at the specified time. @@ -11603,10 +11708,10 @@ declare class Pin { * you need to use `pin.write(0)` or `pinMode(pin, 'output')` first. * * @param {boolean} value - Whether to set output high (true/1) or low (false/0) - * @param {number} time - Time at which to write + * @param {number} time - Time at which to write (in seconds) * @url http://www.espruino.com/Reference#l_Pin_writeAtTime */ - writeAtTime(value: boolean, time: number): void; + writeAtTime(value: ShortBoolean, time: number): void; /** * Return the current mode of the given pin. See `pinMode` for more information. @@ -11646,7 +11751,7 @@ declare class Pin { * @param {any} time - A time in milliseconds, or an array of times (in which case a square wave will be output starting with a pulse of 'value') * @url http://www.espruino.com/Reference#l_Pin_pulse */ - pulse(value: boolean, time: any): void; + pulse(value: ShortBoolean, time: any): void; /** * (Added in 2v20) Get the analogue value of the given pin. See `analogRead` for more information. @@ -11676,12 +11781,14 @@ declare class Pin { * Get information about this pin and its capabilities. Of the form: * ``` * { - * "port" : "A", // the Pin's port on the chip - * "num" : 12, // the Pin's number - * "in_addr" : 0x..., // (if available) the address of the pin's input address in bit-banded memory (can be used with peek) - * "out_addr" : 0x..., // (if available) the address of the pin's output address in bit-banded memory (can be used with poke) - * "analog" : { ADCs : [1], channel : 12 }, // If analog input is available - * "functions" : { + * "port" : "A", // the Pin's port on the chip + * "num" : 12, // the Pin's number + * "mode" : (2v25+) // string: the pin's mode (same as Pin.getMode()) + * "output" : (2v25+) // 0/1: the state of the pin's output register + * "in_addr" : 0x..., // (if available) the address of the pin's input address in bit-banded memory (can be used with peek) + * "out_addr" : 0x..., // (if available) the address of the pin's output address in bit-banded memory (can be used with poke) + * "analog" : { ADCs : [1], channel : 12 }, // If analog input is available + * "functions" : { * "TIM1":{type:"CH1, af:0}, * "I2C3":{type:"SCL", af:1} * } @@ -11716,6 +11823,67 @@ declare const Boolean: BooleanConstructor // GLOBALS +/** + * @returns {any} An object containing the pins for the Qwiic connector on Curio `{sda,scl}` + * @url http://www.espruino.com/Reference#l__global_Q + */ +declare const Q: Qwiic; + +/** + * @returns {Pin} The pin for the servo motor + * @url http://www.espruino.com/Reference#l__global_SERVO + */ +declare const SERVO: Pin; + +/** + * @returns {Pin} The pin for the IR LED + * @url http://www.espruino.com/Reference#l__global_IRLED + */ +declare const IRLED: Pin; + +/** + * @returns {Pin} The pin for the left IR sensor + * @url http://www.espruino.com/Reference#l__global_IRL + */ +declare const IRL: Pin; + +/** + * @returns {Pin} The pin for the left motor + * @url http://www.espruino.com/Reference#l__global_ML1 + */ +declare const ML1: Pin; + +/** + * @returns {Pin} The pin for the left motor + * @url http://www.espruino.com/Reference#l__global_ML2 + */ +declare const ML2: Pin; + +/** + * @returns {Pin} The pin for the right IR sensor + * @url http://www.espruino.com/Reference#l__global_IRR + */ +declare const IRR: Pin; + +/** + * @returns {Pin} The pin for the right motor + * @url http://www.espruino.com/Reference#l__global_MR1 + */ +declare const MR1: Pin; + +/** + * @returns {Pin} The pin for the right motor + * @url http://www.espruino.com/Reference#l__global_MR2 + */ +declare const MR2: Pin; + +/** + * + * @param {any} col - The colours to use, a 24 element array (8 x RGB) + * @url http://www.espruino.com/Reference#l__global_led + */ +declare function led(col: any): void; + /** * The pin marked SDA on the Arduino pin footprint. This is connected directly to * pin A4. @@ -12042,7 +12210,7 @@ declare function setSleepIndicator(pin: any): void; * @param {boolean} sleep * @url http://www.espruino.com/Reference#l__global_setDeepSleep */ -declare function setDeepSleep(sleep: boolean): void; +declare function setDeepSleep(sleep: ShortBoolean): void; /** * Output current interpreter state in a text form such that it can be copied to a @@ -12120,7 +12288,7 @@ declare function save(): void; * @param {boolean} clearFlash - Remove saved code from flash as well * @url http://www.espruino.com/Reference#l__global_reset */ -declare function reset(clearFlash: boolean): void; +declare function reset(clearFlash: ShortBoolean): void; /** * Fill the console with the contents of the given function, so you can edit it. @@ -12140,7 +12308,7 @@ declare function edit(funcName: any): void; * @param {boolean} echoOn * @url http://www.espruino.com/Reference#l__global_echo */ -declare function echo(echoOn: boolean): void; +declare function echo(echoOn: ShortBoolean): void; /** * Return the current system time in Seconds (as a floating point number) @@ -12541,7 +12709,7 @@ declare function shiftOut(pins: Pin | Pin[], options: { clk?: Pin, clkPol?: bool * // setting irq:true will call that function in the interrupt itself * irq : false(default) * // Advanced: If specified, the given pin will be read whenever the watch is called - * // and the state will be included as a 'data' field in the callback + * // and the state will be included as a 'data' field in the callback (`debounce:0` is required) * data : pin * // Advanced: On Nordic devices, a watch may be 'high' or 'low' accuracy. By default low * // accuracy is used (which is better for power consumption), but this means that @@ -12558,7 +12726,7 @@ declare function shiftOut(pins: Pin | Pin[], options: { clk?: Pin, clkPol?: bool * When using `edge:'rising'` or `edge:'falling'`, this is not the same as when * the function was last called. * * `data` is included if `data:pin` was specified in the options, and can be - * used for reading in clocked data + * used for reading in clocked data. It will only work if `debounce:0` is used * For instance, if you want to measure the length of a positive pulse you could * use `setWatch(function(e) { console.log(e.time-e.lastTime); }, BTN, { * repeat:true, edge:'falling' });`. This will only be called on the falling edge @@ -12597,6 +12765,16 @@ declare function clearWatch(id: number): void; declare function clearWatch(): void; declare const global: { + Q: typeof Q; + SERVO: typeof SERVO; + IRLED: typeof IRLED; + IRL: typeof IRL; + ML1: typeof ML1; + ML2: typeof ML2; + IRR: typeof IRR; + MR1: typeof MR1; + MR2: typeof MR2; + led: typeof led; SDA: typeof SDA; SCL: typeof SCL; show: typeof show; @@ -13032,7 +13210,7 @@ declare module "ESP8266" { * @param {boolean} enable - Enable or disable the debug logging. * @url http://www.espruino.com/Reference#l_ESP8266_logDebug */ - function logDebug(enable: boolean): void; + function logDebug(enable: ShortBoolean): void; /** * Set the debug logging mode. It can be disabled (which frees ~1.2KB of heap), @@ -14590,7 +14768,7 @@ declare module "Storage" { * @param {boolean} [showMessage] - [optional] If true, an overlay message will be displayed on the screen while compaction is happening. Default is false. * @url http://www.espruino.com/Reference#l_Storage_compact */ - function compact(showMessage?: boolean): void; + function compact(showMessage?: ShortBoolean): void; /** * This writes information about all blocks in flash memory to the console - and is @@ -14609,7 +14787,7 @@ declare module "Storage" { * @returns {number} The amount of free bytes * @url http://www.espruino.com/Reference#l_Storage_getFree */ - function getFree(checkInternalFlash: boolean): number; + function getFree(checkInternalFlash: ShortBoolean): number; /** * Returns: @@ -14629,7 +14807,7 @@ declare module "Storage" { * @returns {any} An object containing info about the current Storage system * @url http://www.espruino.com/Reference#l_Storage_getStats */ - function getStats(checkInternalFlash: boolean): any; + function getStats(checkInternalFlash: ShortBoolean): any; /** * Writes a lookup table for files into Bangle.js's storage. This allows any file diff --git a/typescript/types/modules.d.ts b/typescript/types/modules.d.ts index e8aa15ac1e..3ba43d38ba 100644 --- a/typescript/types/modules.d.ts +++ b/typescript/types/modules.d.ts @@ -6,3 +6,5 @@ declare function require(moduleName: "ClockFace"): typeof ClockFace_.ClockFace; declare function require(moduleName: "clock_info"): typeof ClockInfo; declare function require(moduleName: "Layout"): typeof Layout.Layout; declare function require(moduleName: "power_usage"): PowerUsageModule; +declare function require(moduleName: "exstats"): typeof ExStats; +declare function require(moduleName: "time_utils"): typeof TimeUtils; diff --git a/typescript/types/time_utils.d.ts b/typescript/types/time_utils.d.ts new file mode 100644 index 0000000000..c176ffac6c --- /dev/null +++ b/typescript/types/time_utils.d.ts @@ -0,0 +1,18 @@ +declare module TimeUtils { + type TimeObj = { + d: number, + h: number, + m: number, + s: number, + }; + + function encodeTime(time: TimeObj): number; + + function decodeTime(millis: number): TimeObj; + + function formatTime(value: number | TimeObj): string + + function formatDuration(value: number | TimeObj, compact?: boolean): string; + + function getCurrentTimeMillis(): number; +} diff --git a/typescript/types/utility.d.ts b/typescript/types/utility.d.ts new file mode 100644 index 0000000000..9deabd1185 --- /dev/null +++ b/typescript/types/utility.d.ts @@ -0,0 +1,5 @@ +type Omit = Pick>; + +type Pick = { + [P in K]: T[P]; +};