From 8482ccc9d553b9ec9b53786ff845f014d4a2d329 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sat, 19 Oct 2024 08:36:12 +0200 Subject: [PATCH 1/6] Performance tuning for number formatting (#320) * Draft: try with and without locale. * Adjusting implementation and testing it. * Updating QA snapshot. * Handle negative values as well. * Draft: performance tuning for number formatting. * Build snapshot update. * Covering calls for resolver. * Allowing UMD globals in tsconfig for lodash case. * REF: moving attempts iteration into memoized function. * Ref: readability. * Ref: better constraints. * Ref: jsdoc. * Updating build snapshot. --- ui/__snapshots__/qa.spec.ts.snap | 45 +++++++++++++++---------- ui/helpers/countdown.spec.ts | 50 +++++++++++++++++++--------- ui/helpers/countdown.ts | 57 +++++++++++++++++++++----------- ui/mocks/lodash.ts | 7 ++++ ui/octorelay.spec.ts | 4 ++- ui/package.json | 1 + ui/tsconfig.json | 3 +- ui/types/OctoPrint.d.ts | 2 ++ ui/yarn.lock | 5 +++ 9 files changed, 120 insertions(+), 54 deletions(-) create mode 100644 ui/mocks/lodash.ts diff --git a/ui/__snapshots__/qa.spec.ts.snap b/ui/__snapshots__/qa.spec.ts.snap index a2779860..a87964d0 100644 --- a/ui/__snapshots__/qa.spec.ts.snap +++ b/ui/__snapshots__/qa.spec.ts.snap @@ -106,7 +106,26 @@ var closeIconHTML = ''; var closeBtnHTML = \`\`; // helpers/countdown.ts -var formatDeadline = (time, locales = [LOCALE, void 0]) => { +var createNumberFormat = _.memoize( + (...[requested, options]) => { + const locales = [requested]; + if (typeof requested === "string" && requested.includes("_")) { + locales.push(requested.replaceAll("_", "-")); + } + locales.push(void 0); + for (const locale of locales) { + try { + return new Intl.NumberFormat(locale, options); + } catch (error) { + console.warn(\`Failed to format time using \${locale} locale\`, error); + } + } + const format = (value) => \`\${value} \${options == null ? void 0 : options.unit}\${value === 1 ? "" : "s"}\`; + return { format }; + }, + (...[locale, options]) => [locale, options == null ? void 0 : options.unit, options == null ? void 0 : options.maximumFractionDigits].join("|") +); +var formatDeadline = (time) => { let unit = "second"; let timeLeft = (time - Date.now()) / 1e3; if (timeLeft >= 60) { @@ -118,22 +137,14 @@ var formatDeadline = (time, locales = [LOCALE, void 0]) => { unit = "hour"; } const isLastMinute = unit === "minute" && timeLeft < 2; - const nonNegTimeLeft = Math.max(0, timeLeft); - for (const locale of locales) { - try { - const formattedTimeLeft = new Intl.NumberFormat(locale, { - style: "unit", - unitDisplay: "long", - minimumFractionDigits: isLastMinute ? 1 : 0, - maximumFractionDigits: isLastMinute ? 1 : 0, - unit - }).format(nonNegTimeLeft); - return \`in \${formattedTimeLeft}\`; - } catch (error) { - console.warn(\`Failed to format time using \${locale} locale\`, error); - } - } - return \`in \${nonNegTimeLeft} \${unit}\${nonNegTimeLeft === 1 ? "" : "s"}\`; + const formattedTimeLeft = createNumberFormat(LOCALE, { + style: "unit", + unitDisplay: "long", + minimumFractionDigits: isLastMinute ? 1 : 0, + maximumFractionDigits: isLastMinute ? 1 : 0, + unit + }).format(Math.max(0, timeLeft)); + return \`in \${formattedTimeLeft}\`; }; var getCountdownDelay = (deadline) => deadline - Date.now() > 12e4 ? 6e4 : 1e3; var setCountdown = (selector, deadline) => { diff --git a/ui/helpers/countdown.spec.ts b/ui/helpers/countdown.spec.ts index 1389793c..3e76d4d4 100644 --- a/ui/helpers/countdown.spec.ts +++ b/ui/helpers/countdown.spec.ts @@ -1,6 +1,7 @@ import MockDate from "mockdate"; +import assert from "node:assert"; import { elementMock, jQueryMock } from "../mocks/jQuery"; -import { formatDeadline, getCountdownDelay, setCountdown } from "./countdown"; +import { lodashMock } from "../mocks/lodash"; import { describe, vi, @@ -11,7 +12,8 @@ import { test, } from "vitest"; -describe("Countdown helpers", () => { +describe("Countdown helpers", async () => { + const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); const setIntervalMock = vi.fn<(handler: () => void, delay: number) => void>( () => "mockedInterval", ); @@ -20,15 +22,24 @@ describe("Countdown helpers", () => { Object.assign(global, { LOCALE: "en", $: jQueryMock, + _: lodashMock, setInterval: setIntervalMock, clearInterval: clearIntervalMock, }); + const { formatDeadline, getCountdownDelay, setCountdown } = await import( + "./countdown" + ); + beforeAll(() => { MockDate.set("2023-08-13T22:30:00"); }); afterEach(() => { + Object.assign(global, { + LOCALE: "en", + }); + warnSpy.mockClear(); setIntervalMock.mockClear(); clearIntervalMock.mockClear(); elementMock.text.mockClear(); @@ -44,21 +55,15 @@ describe("Countdown helpers", () => { "Should format the supplied UNIX timestamp having offset %s seconds", (offset) => { expect(formatDeadline(Date.now() + offset * 1000)).toMatchSnapshot(); + expect(warnSpy).not.toHaveBeenCalled(); }, ); - test.each([ - [10000, "10 seconds"], - [60000, "1 minute"], - [-10000, "0 seconds"], - ])(`should handle invalid locales %#`, (offset, label) => { - const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); - expect( - formatDeadline(Date.now() + offset, [ - "invalid_locale_id", - "another_invalid_one", - ]), - ).toBe(`in ${label}`); + test("should handle invalid locales", () => { + Object.assign(global, { + LOCALE: "invalid_locale_id", + }); + expect(formatDeadline(Date.now() + 10000)).toBe(`in 10 seconds`); expect(warnSpy).toHaveBeenCalledTimes(2); expect(warnSpy.mock.calls).toEqual([ [ @@ -66,11 +71,26 @@ describe("Countdown helpers", () => { expect.any(Error), ], [ - "Failed to format time using another_invalid_one locale", + "Failed to format time using invalid-locale-id locale", expect.any(Error), ], ]); }); + + test.each([ + [1000, "1 second"], + [10000, "10 seconds"], + ])("should handle complete Intl malfunction", (offset, expected) => { + vi.spyOn(Intl, "NumberFormat").mockImplementation(() => + assert.fail("Can not do this"), + ); + expect(formatDeadline(Date.now() + offset)).toBe(`in ${expected}`); + expect(warnSpy).toHaveBeenCalledTimes(2); + expect(warnSpy.mock.calls).toEqual([ + ["Failed to format time using en locale", expect.any(Error)], + ["Failed to format time using undefined locale", expect.any(Error)], + ]); + }); }); describe("getCountdownDelay() helper", () => { diff --git a/ui/helpers/countdown.ts b/ui/helpers/countdown.ts index 1903a6a8..3834e4df 100644 --- a/ui/helpers/countdown.ts +++ b/ui/helpers/countdown.ts @@ -1,7 +1,32 @@ -export const formatDeadline = ( - time: number, - locales = [LOCALE, undefined], -): string => { +/** + * @desc Creating Intl.NumberFormat is relatively slow, therefore using memoize() per set of arguments + * @since 5.1.0 also iterating over the requested locale, fixed locale and default one, then falling back to custom + * */ +const createNumberFormat = _.memoize( + ( + ...[requested, options]: Parameters + ): Pick => { + const locales = [requested]; + if (typeof requested === "string" && requested.includes("_")) { + locales.push(requested.replaceAll("_", "-")); + } + locales.push(undefined); + for (const locale of locales) { + try { + return new Intl.NumberFormat(locale, options); + } catch (error) { + console.warn(`Failed to format time using ${locale} locale`, error); + } + } + const format = (value: number) => + `${value} ${options?.unit}${value === 1 ? "" : "s"}`; + return { format }; + }, + (...[locale, options]: Parameters) => + [locale, options?.unit, options?.maximumFractionDigits].join("|"), +); + +export const formatDeadline = (time: number): string => { let unit: "second" | "minute" | "hour" = "second"; let timeLeft = (time - Date.now()) / 1000; if (timeLeft >= 60) { @@ -13,22 +38,14 @@ export const formatDeadline = ( unit = "hour"; } const isLastMinute = unit === "minute" && timeLeft < 2; - const nonNegTimeLeft = Math.max(0, timeLeft); - for (const locale of locales) { - try { - const formattedTimeLeft = new Intl.NumberFormat(locale, { - style: "unit", - unitDisplay: "long", - minimumFractionDigits: isLastMinute ? 1 : 0, - maximumFractionDigits: isLastMinute ? 1 : 0, - unit, - }).format(nonNegTimeLeft); - return `in ${formattedTimeLeft}`; - } catch (error) { - console.warn(`Failed to format time using ${locale} locale`, error); - } - } - return `in ${nonNegTimeLeft} ${unit}${nonNegTimeLeft === 1 ? "" : "s"}`; + const formattedTimeLeft = createNumberFormat(LOCALE, { + style: "unit", + unitDisplay: "long", + minimumFractionDigits: isLastMinute ? 1 : 0, + maximumFractionDigits: isLastMinute ? 1 : 0, + unit, + }).format(Math.max(0, timeLeft)); + return `in ${formattedTimeLeft}`; }; export const getCountdownDelay = (deadline: number): number => diff --git a/ui/mocks/lodash.ts b/ui/mocks/lodash.ts new file mode 100644 index 00000000..356393f4 --- /dev/null +++ b/ui/mocks/lodash.ts @@ -0,0 +1,7 @@ +import { vi } from "vitest"; + +export const lodashMock = { + memoize: vi.fn( + (fn: () => unknown, resolver: () => unknown) => resolver() && fn, + ), +}; diff --git a/ui/octorelay.spec.ts b/ui/octorelay.spec.ts index 2417a9ee..3f7a92b4 100644 --- a/ui/octorelay.spec.ts +++ b/ui/octorelay.spec.ts @@ -1,12 +1,14 @@ -import { initOctoRelayModel } from "./model/initOctoRelayModel"; +import { lodashMock } from "./mocks/lodash"; import { describe, vi, test, expect } from "vitest"; describe("Entrypoint", () => { const jQueryMock = vi.fn(); Object.assign(global, { $: jQueryMock, + _: lodashMock, }); test("Should set the document onLoad handler", async () => { + const { initOctoRelayModel } = await import("./model/initOctoRelayModel"); await import("./octorelay"); expect(jQueryMock).toHaveBeenCalledWith(initOctoRelayModel); }); diff --git a/ui/package.json b/ui/package.json index 7e89125a..5c361f6f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -21,6 +21,7 @@ "devDependencies": { "@tsconfig/node18": "^18.2.4", "@types/jquery": "^3.5.30", + "@types/lodash": "^3.10.9", "@types/node": "^22.5.2", "@vitest/coverage-istanbul": "^2.0.5", "eslint": "^9.12.0", diff --git a/ui/tsconfig.json b/ui/tsconfig.json index 02e042ea..90a28d84 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -4,7 +4,8 @@ "target": "ES6", "module": "ES2022", "moduleResolution": "Bundler", - "removeComments": true + "removeComments": true, + "allowUmdGlobalAccess": true }, "exclude": [ "node_modules" diff --git a/ui/types/OctoPrint.d.ts b/ui/types/OctoPrint.d.ts index c2b1dec8..1388e973 100644 --- a/ui/types/OctoPrint.d.ts +++ b/ui/types/OctoPrint.d.ts @@ -1,4 +1,5 @@ /// +/// interface JQuery { /** @see https://github.com/OctoPrint/OctoPrint/blob/1.9.0/src/octoprint/static/js/lib/bootstrap/bootstrap-modal.js */ @@ -37,6 +38,7 @@ interface ViewModel { } declare const OCTOPRINT_VIEWMODELS: Array; +/** @see https://github.com/OctoPrint/OctoPrint/blob/1.5.3/src/octoprint/templates/initscript.jinja2#L32 */ declare const LOCALE: string; /** @see https://github.com/OctoPrint/OctoPrint/blob/1.9.0/docs/jsclientlib/base.rst */ diff --git a/ui/yarn.lock b/ui/yarn.lock index 61b1ff10..9296cc87 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -743,6 +743,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/lodash@^3.10.9": + version "3.10.9" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-3.10.9.tgz#8e2276d0b1d60a0d3820268f85705f77ec482c5e" + integrity sha512-BqzpDcDNIj0D15QezYF2C8OhOZVsm3g9Y6bLfXC6jO+wgxDGdCJlzge9OqnI0RezYJNsfsGXovhb3zTZjHpiCA== + "@types/node@^22.5.2": version "22.7.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" From 4bc6d98ee57d94f70a3346bfa26e21bced8cd1c3 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sat, 19 Oct 2024 08:53:26 +0200 Subject: [PATCH 2/6] Enabling vitest globals. (#322) --- ui/bindings.spec.ts | 1 - ui/helpers/actions.spec.ts | 1 - ui/helpers/countdown.spec.ts | 11 +---------- ui/helpers/hints.spec.ts | 1 - ui/helpers/narrowing.spec.ts | 1 - ui/mocks/OctoRelayModel.ts | 2 -- ui/mocks/actions.ts | 2 -- ui/mocks/countdown.ts | 2 -- ui/mocks/hints.ts | 2 -- ui/mocks/jQuery.ts | 2 +- ui/mocks/lodash.ts | 2 -- ui/mocks/messageHandler.ts | 2 -- ui/model/OctoRelayModel.spec.ts | 1 - ui/model/initOctoRelayModel.spec.ts | 1 - ui/model/messageHandler.spec.ts | 2 +- ui/octorelay.spec.ts | 1 - ui/qa.spec.ts | 1 - ui/tsconfig.json | 3 ++- ui/vitest.config.ts | 1 + 19 files changed, 6 insertions(+), 33 deletions(-) diff --git a/ui/bindings.spec.ts b/ui/bindings.spec.ts index 0ec75f7e..b0644cf3 100644 --- a/ui/bindings.spec.ts +++ b/ui/bindings.spec.ts @@ -1,6 +1,5 @@ import { readFileSync } from "node:fs"; import { Window } from "happy-dom"; -import { describe, test, expect } from "vitest"; describe("Knockout bindings", () => { const document = new Window().document; diff --git a/ui/helpers/actions.spec.ts b/ui/helpers/actions.spec.ts index f4778116..c8f9437c 100644 --- a/ui/helpers/actions.spec.ts +++ b/ui/helpers/actions.spec.ts @@ -1,6 +1,5 @@ import { elementMock, jQueryMock } from "../mocks/jQuery"; import { cancelTask, toggleRelay } from "./actions"; -import { vi, describe, afterEach, test, expect } from "vitest"; describe("Actions", () => { const apiMock = vi.fn(); diff --git a/ui/helpers/countdown.spec.ts b/ui/helpers/countdown.spec.ts index 3e76d4d4..73652b29 100644 --- a/ui/helpers/countdown.spec.ts +++ b/ui/helpers/countdown.spec.ts @@ -1,16 +1,7 @@ import MockDate from "mockdate"; -import assert from "node:assert"; +import assert from "node:assert/strict"; import { elementMock, jQueryMock } from "../mocks/jQuery"; import { lodashMock } from "../mocks/lodash"; -import { - describe, - vi, - beforeAll, - afterEach, - afterAll, - expect, - test, -} from "vitest"; describe("Countdown helpers", async () => { const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); diff --git a/ui/helpers/hints.spec.ts b/ui/helpers/hints.spec.ts index 636b28f9..deade022 100644 --- a/ui/helpers/hints.spec.ts +++ b/ui/helpers/hints.spec.ts @@ -3,7 +3,6 @@ import { countdownMock, deadlineMock, disposerMock } from "../mocks/countdown"; import { cancelMock } from "../mocks/actions"; import { elementMock, jQueryMock } from "../mocks/jQuery"; import { addTooltip, clearHints, addPopover, showHints } from "./hints"; -import { describe, beforeAll, afterAll, afterEach, test, expect } from "vitest"; describe("Hints helpers", () => { Object.assign(global, { diff --git a/ui/helpers/narrowing.spec.ts b/ui/helpers/narrowing.spec.ts index 80ead728..83feaa23 100644 --- a/ui/helpers/narrowing.spec.ts +++ b/ui/helpers/narrowing.spec.ts @@ -1,5 +1,4 @@ import { hasUpcomingTask } from "./narrowing"; -import { describe, test, expect } from "vitest"; describe("Narrowing helpers", () => { describe("hasUpcomingTask() helper", () => { diff --git a/ui/mocks/OctoRelayModel.ts b/ui/mocks/OctoRelayModel.ts index 71e771d6..eec58c4f 100644 --- a/ui/mocks/OctoRelayModel.ts +++ b/ui/mocks/OctoRelayModel.ts @@ -1,5 +1,3 @@ -import { vi } from "vitest"; - export const modelMock = vi.fn(); vi.mock("../model/OctoRelayModel", () => ({ OctoRelayViewModel: modelMock })); diff --git a/ui/mocks/actions.ts b/ui/mocks/actions.ts index ea648ec7..0e1d4eec 100644 --- a/ui/mocks/actions.ts +++ b/ui/mocks/actions.ts @@ -1,5 +1,3 @@ -import { vi } from "vitest"; - export const cancelMock = vi.fn(); export const toggleMock = vi.fn(); diff --git a/ui/mocks/countdown.ts b/ui/mocks/countdown.ts index 75517705..39d41247 100644 --- a/ui/mocks/countdown.ts +++ b/ui/mocks/countdown.ts @@ -1,5 +1,3 @@ -import { vi } from "vitest"; - export const disposerMock = vi.fn(); export const countdownMock = vi.fn(() => disposerMock); export const deadlineMock = vi.fn(() => "sample deadline"); diff --git a/ui/mocks/hints.ts b/ui/mocks/hints.ts index b3506ef0..eda1a3d5 100644 --- a/ui/mocks/hints.ts +++ b/ui/mocks/hints.ts @@ -1,5 +1,3 @@ -import { vi } from "vitest"; - export const clearMock = vi.fn(); export const showMock = vi.fn(); diff --git a/ui/mocks/jQuery.ts b/ui/mocks/jQuery.ts index d9d8c9ee..8d5dc6db 100644 --- a/ui/mocks/jQuery.ts +++ b/ui/mocks/jQuery.ts @@ -1,4 +1,4 @@ -import { vi, type Mock } from "vitest"; +import type { Mock } from "vitest"; export const elementMock: Record< | "toggle" diff --git a/ui/mocks/lodash.ts b/ui/mocks/lodash.ts index 356393f4..841e11d4 100644 --- a/ui/mocks/lodash.ts +++ b/ui/mocks/lodash.ts @@ -1,5 +1,3 @@ -import { vi } from "vitest"; - export const lodashMock = { memoize: vi.fn( (fn: () => unknown, resolver: () => unknown) => resolver() && fn, diff --git a/ui/mocks/messageHandler.ts b/ui/mocks/messageHandler.ts index bcbfc7e2..f0d8eb6e 100644 --- a/ui/mocks/messageHandler.ts +++ b/ui/mocks/messageHandler.ts @@ -1,5 +1,3 @@ -import { vi } from "vitest"; - export const handlerMock = vi.fn(); vi.mock("../model/messageHandler", () => ({ diff --git a/ui/model/OctoRelayModel.spec.ts b/ui/model/OctoRelayModel.spec.ts index a5ce0e36..33ed5539 100644 --- a/ui/model/OctoRelayModel.spec.ts +++ b/ui/model/OctoRelayModel.spec.ts @@ -1,7 +1,6 @@ import { handlerMock } from "../mocks/messageHandler"; import type { OwnModel, OwnProperties } from "../types/OwnModel"; import { OctoRelayViewModel } from "./OctoRelayModel"; -import { describe, test, vi, expect } from "vitest"; describe("OctoRelayViewModel", () => { test("should set certain props of itself", () => { diff --git a/ui/model/initOctoRelayModel.spec.ts b/ui/model/initOctoRelayModel.spec.ts index fa5d6c82..e74c6369 100644 --- a/ui/model/initOctoRelayModel.spec.ts +++ b/ui/model/initOctoRelayModel.spec.ts @@ -1,6 +1,5 @@ import { modelMock } from "../mocks/OctoRelayModel"; import { initOctoRelayModel } from "./initOctoRelayModel"; -import { describe, test, expect } from "vitest"; describe("initOctorelayModel()", () => { const registryMock: ViewModel[] = []; diff --git a/ui/model/messageHandler.spec.ts b/ui/model/messageHandler.spec.ts index 033bfc79..2621fce9 100644 --- a/ui/model/messageHandler.spec.ts +++ b/ui/model/messageHandler.spec.ts @@ -1,9 +1,9 @@ +import type { Mock } from "vitest"; import { elementMock, jQueryMock } from "../mocks/jQuery"; import type { OwnModel, OwnProperties } from "../types/OwnModel"; import { clearMock, showMock } from "../mocks/hints"; import { toggleMock } from "../mocks/actions"; import { makeMessageHandler } from "./messageHandler"; -import { describe, vi, type Mock, afterEach, test, expect } from "vitest"; describe("makeMessageHandler()", () => { Object.assign(global, { diff --git a/ui/octorelay.spec.ts b/ui/octorelay.spec.ts index 3f7a92b4..2e880110 100644 --- a/ui/octorelay.spec.ts +++ b/ui/octorelay.spec.ts @@ -1,5 +1,4 @@ import { lodashMock } from "./mocks/lodash"; -import { describe, vi, test, expect } from "vitest"; describe("Entrypoint", () => { const jQueryMock = vi.fn(); diff --git a/ui/qa.spec.ts b/ui/qa.spec.ts index 3aef05f5..344dd9c5 100644 --- a/ui/qa.spec.ts +++ b/ui/qa.spec.ts @@ -1,5 +1,4 @@ import { readFile } from "node:fs/promises"; -import { describe, test, expect } from "vitest"; describe("QA", () => { test.each(["css/octorelay.css", "js/octorelay.js"])( diff --git a/ui/tsconfig.json b/ui/tsconfig.json index 90a28d84..5403d702 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -5,7 +5,8 @@ "module": "ES2022", "moduleResolution": "Bundler", "removeComments": true, - "allowUmdGlobalAccess": true + "allowUmdGlobalAccess": true, + "types": ["vitest/globals"] }, "exclude": [ "node_modules" diff --git a/ui/vitest.config.ts b/ui/vitest.config.ts index ddcb7e6e..313fd64a 100644 --- a/ui/vitest.config.ts +++ b/ui/vitest.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { + globals: true, coverage: { provider: "istanbul", reporter: ["json", "lcov", "text", "html"], From f23f952487b47c22493ead724700f0b14b56aaa9 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Sat, 19 Oct 2024 09:05:51 +0200 Subject: [PATCH 3/6] Changelog: 5.1.0. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b10732a..a52aafce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Version 5 +### 5.1.0 + +- Performance improvement for the countdown (remaining time formatting function): + - Attempting to fix user defined locale (`it_IT —> it-IT`) in order to preserve translations; + - Looking up for a suitable locale only once per time unit (memoization); + - This should make the countdown about 47 times more efficient. + ### 5.0.3 - Fixed issue with incorrect locale format causing countdown failure and disability to cancel upcoming relay switch: From ccb49eb5080b18aa534e3b0119ae386afa43bf25 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sat, 19 Oct 2024 09:55:41 +0200 Subject: [PATCH 4/6] Avoid pin lock, release immediately after creation. (#315) --- octoprint_octorelay/driver.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/octoprint_octorelay/driver.py b/octoprint_octorelay/driver.py index f2a5503a..e6b02d44 100755 --- a/octoprint_octorelay/driver.py +++ b/octoprint_octorelay/driver.py @@ -12,6 +12,8 @@ def __init__(self, pin: int, inverted: bool, pin_factory=None): self.pin = pin # GPIO pin self.inverted = inverted # marks the relay as normally closed self.handle = LED(pin, pin_factory=pin_factory, initial_value=inverted) + # release immediately, avoid lock, allow physical buttons to operate same relays: + self.handle.pin_factory.release_pins(self.handle, self.pin) def __repr__(self) -> str: return f"{type(self).__name__}(pin={self.pin},inverted={self.inverted},closed={self.is_closed()})" From 1ad9844f893b231935ead9f04ac030e64f372354 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Sat, 26 Oct 2024 23:30:46 +0200 Subject: [PATCH 5/6] Changelog: fixed physical button plugin compatibility. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a52aafce..2e11b872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ ### 5.1.0 +- Fixed compatibility issue with [Physical Button plugin](https://github.com/LuxuSam/PhysicalButton): + - OctoRelay v5 uses a new driver having exclusive pin reservation, so that two plugins could not operate same pin. + - This version releases the reservation immediately, enabling relay operation both using the UI and a physical button. - Performance improvement for the countdown (remaining time formatting function): - Attempting to fix user defined locale (`it_IT —> it-IT`) in order to preserve translations; - Looking up for a suitable locale only once per time unit (memoization); From 7bd4f63ab54e6d30660de9f4cc963437deb65573 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Sat, 26 Oct 2024 23:31:22 +0200 Subject: [PATCH 6/6] Minor: semicolon. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e11b872..05d116c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### 5.1.0 - Fixed compatibility issue with [Physical Button plugin](https://github.com/LuxuSam/PhysicalButton): - - OctoRelay v5 uses a new driver having exclusive pin reservation, so that two plugins could not operate same pin. + - OctoRelay v5 uses a new driver having exclusive pin reservation, so that two plugins could not operate same pin; - This version releases the reservation immediately, enabling relay operation both using the UI and a physical button. - Performance improvement for the countdown (remaining time formatting function): - Attempting to fix user defined locale (`it_IT —> it-IT`) in order to preserve translations;