From 7334bd126a7c11ef6f219818bf8f4f59db769cd7 Mon Sep 17 00:00:00 2001 From: The00Dustin <91274634+The00Dustin@users.noreply.github.com> Date: Thu, 24 Oct 2024 04:53:25 -0400 Subject: [PATCH 01/16] fix(logging): more accurate idle/busy queue logging (#7309) Co-authored-by: Dominic Griesel --- packages/zwave-js/src/lib/driver/Driver.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/zwave-js/src/lib/driver/Driver.ts b/packages/zwave-js/src/lib/driver/Driver.ts index ace5128f2ff0..947deff90ca8 100644 --- a/packages/zwave-js/src/lib/driver/Driver.ts +++ b/packages/zwave-js/src/lib/driver/Driver.ts @@ -715,7 +715,9 @@ export class Driver extends TypedEventEmitter } private set queueIdle(value: boolean) { if (this._queueIdle !== value) { - this.driverLog.print(`all queues ${value ? "idle" : "busy"}`); + this.driverLog.print( + value ? "all queues idle" : "one or more queues busy", + ); this._queueIdle = value; this.handleQueueIdleChange(value); } From 894481d36c0883dd76f4224deb9ae1ec1ba8b561 Mon Sep 17 00:00:00 2001 From: xuexiaofei <279716582@qq.com> Date: Thu, 24 Oct 2024 19:32:27 +0800 Subject: [PATCH 02/16] feat(config): add ZVIDAR ZW882 Z-Wave Gateway (#7310) --- packages/config/config/devices/0x045a/zw882.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/config/config/devices/0x045a/zw882.json diff --git a/packages/config/config/devices/0x045a/zw882.json b/packages/config/config/devices/0x045a/zw882.json new file mode 100644 index 000000000000..897b19a264fc --- /dev/null +++ b/packages/config/config/devices/0x045a/zw882.json @@ -0,0 +1,16 @@ +{ + "manufacturer": "ZVIDAR", + "manufacturerId": "0x045a", + "label": "ZW882", + "description": "Gateway", + "devices": [ + { + "productType": "0x0004", + "productId": "0x0371" + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + } +} From 70a34793dfb1c3f11a9196dc0375cafb55483f1f Mon Sep 17 00:00:00 2001 From: xuexiaofei <279716582@qq.com> Date: Thu, 24 Oct 2024 19:32:45 +0800 Subject: [PATCH 03/16] feat(config): add ZVIDAR ZW881 Multi-Protocol Gateway (#7311) --- packages/config/config/devices/0x045a/zw881.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/config/config/devices/0x045a/zw881.json diff --git a/packages/config/config/devices/0x045a/zw881.json b/packages/config/config/devices/0x045a/zw881.json new file mode 100644 index 000000000000..44c6f4c7a3c8 --- /dev/null +++ b/packages/config/config/devices/0x045a/zw881.json @@ -0,0 +1,16 @@ +{ + "manufacturer": "ZVIDAR", + "manufacturerId": "0x045a", + "label": "ZW881", + "description": "Multi-Protocol Gateway", + "devices": [ + { + "productType": "0x0004", + "productId": "0x0371" + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + } +} From 4bf6a575a09086f429ab18153b2b04cfd96b8f38 Mon Sep 17 00:00:00 2001 From: The00Dustin <91274634+The00Dustin@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:34:13 -0400 Subject: [PATCH 04/16] fix(config): add include, exclude, and wakeup instructions for VCZ1 (#7307) Co-authored-by: Dominic Griesel --- packages/config/config/devices/0x026e/vcz1.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/config/config/devices/0x026e/vcz1.json b/packages/config/config/devices/0x026e/vcz1.json index dd0c5051b83e..2af85c40477f 100644 --- a/packages/config/config/devices/0x026e/vcz1.json +++ b/packages/config/config/devices/0x026e/vcz1.json @@ -15,7 +15,10 @@ "max": "255.255" }, "metadata": { - "reset": "Press and hold the button on the control for approximately 15 seconds (the LED will stop flashing when complete.\n\n\"Please use this procedure only when the network primary controller is missing or otherwise inoperable.\"", + "inclusion": "Press and hold the programming button until the LED light bar begins to flash green then release the button.", + "exclusion": "Press and hold the programming button until the LED light bar begins to flash green then release the button.", + "reset": "Press and hold the programming button until the LEDs stop blinking. LEDs will blink green, amber, red, and then finally turn off (about 15 seconds). Local Reset must be performed on both the controllers and motors and should only be used if the primary controller is no longer available.", + "wakeup": "Press and release the programming button one time.", "manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/1786/Graber%20Virtual%20Cord%20Owner's%20Manual.pdf" }, "compat": { From 2a911d41ac66b429b35e492bb8e6aab149dde388 Mon Sep 17 00:00:00 2001 From: truffles003 <55584145+truffles003@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:39:07 +0200 Subject: [PATCH 05/16] feat(config): add new Product ID to Namron 16A Switch (#7301) --- packages/config/config/devices/0x0438/4512746.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/config/config/devices/0x0438/4512746.json b/packages/config/config/devices/0x0438/4512746.json index 7ce830056195..bdbc8560818a 100644 --- a/packages/config/config/devices/0x0438/4512746.json +++ b/packages/config/config/devices/0x0438/4512746.json @@ -7,6 +7,10 @@ { "productType": "0x0004", "productId": "0xd019" + }, + { + "productType": "0x0200", + "productId": "0xd006" } ], "firmwareVersion": { From ef68392730063db469a2f6a03d865d023523a77d Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Thu, 24 Oct 2024 05:44:42 -0600 Subject: [PATCH 06/16] feat(config): add Minoston MP24Z 800LR Outdoor Smart Plug - 2 Outlet (#7302) --- .../config/devices/0x0312/mp24z_800.json | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 packages/config/config/devices/0x0312/mp24z_800.json diff --git a/packages/config/config/devices/0x0312/mp24z_800.json b/packages/config/config/devices/0x0312/mp24z_800.json new file mode 100644 index 000000000000..ebbd3152137b --- /dev/null +++ b/packages/config/config/devices/0x0312/mp24z_800.json @@ -0,0 +1,51 @@ +{ + "manufacturer": "Minoston", + "manufacturerId": "0x0312", + "label": "MP24Z", + "description": "Outdoor Smart Plug - 2 Outlet (800S)", + "devices": [ + { + "productType": "0xff01", + "productId": "0xff97", + "zwaveAllianceId": 3719 + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "paramInformation": [ + { + "#": "1", + "$import": "~/templates/master_template.json#led_indicator_three_options_inverted" + }, + { + "#": "2", + "$import": "templates/minoston_template.json#auto_off_timer", + "label": "Outlet 1 (Left): Auto Off Timer" + }, + { + "#": "3", + "$import": "templates/minoston_template.json#auto_off_timer", + "label": "Outlet 2 (Right): Auto Off Timer" + }, + { + "#": "4", + "$import": "templates/minoston_template.json#auto_on_timer", + "label": "Outlet 1 (Left): Auto On Timer" + }, + { + "#": "5", + "$import": "templates/minoston_template.json#auto_on_timer", + "label": "Outlet 2 (Right): Auto On Timer" + }, + { + "#": "6", + "$import": "~/templates/master_template.json#state_after_power_failure_off_on_prev" + }, + { + "#": "7", + "$import": "templates/minoston_template.json#led_indicator_brightness" + } + ] +} From 80fb6f20ae84f29bdf2128114aaa49dc061041e1 Mon Sep 17 00:00:00 2001 From: AlCalzone Date: Thu, 24 Oct 2024 13:49:51 +0200 Subject: [PATCH 07/16] revert: add ZVIDAR ZW882 Z-Wave Gateway (#7317) --- packages/config/config/devices/0x045a/zw882.json | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 packages/config/config/devices/0x045a/zw882.json diff --git a/packages/config/config/devices/0x045a/zw882.json b/packages/config/config/devices/0x045a/zw882.json deleted file mode 100644 index 897b19a264fc..000000000000 --- a/packages/config/config/devices/0x045a/zw882.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "manufacturer": "ZVIDAR", - "manufacturerId": "0x045a", - "label": "ZW882", - "description": "Gateway", - "devices": [ - { - "productType": "0x0004", - "productId": "0x0371" - } - ], - "firmwareVersion": { - "min": "0.0", - "max": "255.255" - } -} From 386923436740914ddd25bfd3ec325e717dc6a457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geir=20R=C3=A5ness?= Date: Thu, 24 Oct 2024 13:56:47 +0200 Subject: [PATCH 08/16] feat(config): add Heatit Z-TEMP3 (#7179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Geir Råness Co-authored-by: Dominic Griesel --- .../config/config/devices/0x019b/z-temp3.json | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 packages/config/config/devices/0x019b/z-temp3.json diff --git a/packages/config/config/devices/0x019b/z-temp3.json b/packages/config/config/devices/0x019b/z-temp3.json new file mode 100644 index 000000000000..fd51e915f81c --- /dev/null +++ b/packages/config/config/devices/0x019b/z-temp3.json @@ -0,0 +1,242 @@ +{ + "manufacturer": "Heatit", + "manufacturerId": "0x019b", + "label": "Z-TEMP3", + "description": "Thermostat", + "devices": [ + { + "productType": "0x0031", + "productId": "0x3101", + "zwaveAllianceId": 4908 + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "associations": { + "1": { + "label": "Lifeline", + "maxNodes": 1, + "isLifeline": true + }, + "2": { + "label": "Binary Switch Set", + "maxNodes": 10 + }, + "3": { + "label": "Thermostat Setpoint Set", + "maxNodes": 10 + }, + "4": { + "label": "Thermostat Mode Set", + "maxNodes": 10 + } + }, + "paramInformation": [ + { + "#": "1", + "$import": "~/templates/master_template.json#base_enable_disable_inverted", + "label": "Local Control", + "defaultValue": 0 + }, + { + "#": "2", + "label": "Min Temp Limit", + "valueSize": 2, + "unit": "0.1 °C", + "minValue": 50, + "maxValue": 400, + "defaultValue": 50 + }, + { + "#": "3", + "label": "Max Temp Limit", + "valueSize": 2, + "unit": "0.1 °C", + "minValue": 50, + "maxValue": 400, + "defaultValue": 400 + }, + { + "#": "4", + "label": "Temp Sensor Calibration", + "valueSize": 1, + "unit": "0.1 °C", + "minValue": -60, + "maxValue": 60, + "defaultValue": 0, + "unsigned": false + }, + { + "#": "5", + "label": "Regulation Mode", + "valueSize": 1, + "defaultValue": 0, + "allowManualEntry": false, + "options": [ + { + "label": "Hysteresis", + "value": 0 + }, + { + "label": "PWM", + "value": 1 + } + ] + }, + { + "#": "6", + "label": "Update Interval", + "description": "Allowable range: 0, 150-2000", + "valueSize": 2, + "unit": "seconds", + "minValue": 0, + "maxValue": 65535, + "defaultValue": 3600, + "unsigned": true, + "options": [ + { + "label": "Only on change", + "value": 0 + } + ] + }, + { + "#": "7", + "label": "Temperature Control Hysteresis", + "valueSize": 1, + "unit": "0.1 °C", + "minValue": 3, + "maxValue": 30, + "defaultValue": 5 + }, + { + "#": "8", + "label": "Temperature Display", + "valueSize": 1, + "defaultValue": 0, + "allowManualEntry": false, + "options": [ + { + "label": "Setpoint", + "value": 0 + }, + { + "label": "Measured", + "value": 1 + } + ] + }, + { + "#": "9", + "label": "Active Display Brightness", + "valueSize": 1, + "unit": "10 %", + "minValue": 1, + "maxValue": 10, + "defaultValue": 10 + }, + { + "#": "10", + "label": "Temperature Report Interval", + "valueSize": 2, + "unit": "seconds", + "minValue": 150, + "maxValue": 65535, + "defaultValue": 990, + "unsigned": true + }, + { + "#": "11", + "label": "Temperature Report Hysteresis", + "valueSize": 1, + "unit": "0.1 °C", + "minValue": 1, + "maxValue": 100, + "defaultValue": 10 + }, + { + "#": "12", + "label": "Humidity Report Interval", + "valueSize": 2, + "unit": "seconds", + "minValue": 150, + "maxValue": 65535, + "defaultValue": 990, + "unsigned": true + }, + { + "#": "13", + "label": "Humidity Report Hysteresis", + "valueSize": 1, + "unit": "%", + "minValue": 1, + "maxValue": 100, + "defaultValue": 25 + }, + { + "#": "14", + "label": "Heating Setpoint", + "valueSize": 2, + "unit": "0.1 °C", + "minValue": 50, + "maxValue": 400, + "defaultValue": 210 + }, + { + "#": "15", + "label": "Cooling Setpoint", + "valueSize": 2, + "unit": "0.1 °C", + "minValue": 50, + "maxValue": 400, + "defaultValue": 250 + }, + { + "#": "16", + "label": "Eco Setpoint", + "valueSize": 2, + "unit": "0.1 °C", + "minValue": 50, + "maxValue": 400, + "defaultValue": 180 + }, + { + "#": "17", + "label": "Operating Mode", + "valueSize": 1, + "defaultValue": 1, + "allowManualEntry": false, + "options": [ + { + "label": "Off", + "value": 0 + }, + { + "label": "Heating", + "value": 1 + }, + { + "label": "Cooling", + "value": 2 + }, + { + "label": "Eco", + "value": 3 + } + ] + }, + { + "#": "18", + "$import": "~/templates/master_template.json#base_enable_disable", + "label": "Open Window Detection" + } + ], + "metadata": { + "inclusion": "Hold the Center button for 5 seconds. The display will show “OFF”\n Press the ”+” button once to see “CON” in the display.\n Start the add/remove device process in your primary controller.\n Start the configuration mode on the thermostat by holding the Center button for approximately 2 seconds.", + "exclusion": "Hold the Center button for 5 seconds. The display will show “OFF”\n Press the ”+” button once to see “CON” in the display.\n Start the add/remove device process in your primary controller.\n Start the configuration mode on the thermostat by holding the Center button for approximately 2 seconds.", + "reset": "Enter the menu by holding the Center button for about 5seconds, navigate in the menu with the ”+” button til you see FACT. Press the Center button until you see “-- --” blinking in the display, then hold for about 5 seconds to perform a reset. You may also initiate a reset by holding the Right and Center buttons for 55 seconds.", + "manual": "https://media.heatit.com/3872" + } +} From 5dd9e9db05cb9089896ce4745d9bdae70491e877 Mon Sep 17 00:00:00 2001 From: xuexiaofei <279716582@qq.com> Date: Thu, 24 Oct 2024 19:57:02 +0800 Subject: [PATCH 09/16] feat(config): add ZVIDAR WM25L Smartwings Smart Motor (#7312) Co-authored-by: Dominic Griesel --- .../config/config/devices/0x045a/WM25L.json | 245 ++++++++++++++++++ .../eslint-plugin/src/utils/wordsAndNames.ts | 1 + 2 files changed, 246 insertions(+) create mode 100644 packages/config/config/devices/0x045a/WM25L.json diff --git a/packages/config/config/devices/0x045a/WM25L.json b/packages/config/config/devices/0x045a/WM25L.json new file mode 100644 index 000000000000..d3c1422014c2 --- /dev/null +++ b/packages/config/config/devices/0x045a/WM25L.json @@ -0,0 +1,245 @@ +{ + "manufacturer": "ZVIDAR", + "manufacturerId": "0x045a", + "label": "WM25L", + "description": "Smartwings Smart Motor", + "devices": [ + { + "productType": "0x0004", + "productId": "0x050b" + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "associations": { + "1": { + "label": "Lifeline", + "maxNodes": 5, + "isLifeline": true + }, + "2": { + "label": "On/Off Control", + "maxNodes": 5 + }, + "3": { + "label": "Multilevel Set", + "maxNodes": 5 + }, + "4": { + "label": "Multilevel Start/Stop", + "maxNodes": 5 + } + }, + "paramInformation": [ + { + "#": "1", + "label": "Enable Manual Pull-Start", + "valueSize": 1, + "defaultValue": 1, + "allowManualEntry": false, + "options": [ + { + "label": "Close", + "value": 0 + }, + { + "label": "Open", + "value": 1 + } + ] + }, + { + "#": "2", + "label": "Set Motor Direction", + "valueSize": 1, + "defaultValue": 1, + "allowManualEntry": false, + "options": [ + { + "label": "Forward", + "value": 1 + }, + { + "label": "Reverse", + "value": 2 + } + ] + }, + { + "#": "3", + "label": "Manually Set Open Borders", + "valueSize": 1, + "defaultValue": 0, + "allowManualEntry": false, + "options": [ + { + "label": "Cancel", + "value": 0 + }, + { + "label": "Start", + "value": 1 + } + ] + }, + { + "#": "4", + "label": "Manually Set Close Borders", + "valueSize": 1, + "defaultValue": 0, + "allowManualEntry": false, + "options": [ + { + "label": "Cancel", + "value": 0 + }, + { + "label": "Start", + "value": 1 + } + ] + }, + { + "#": "5", + "label": "Set Motor Status", + "valueSize": 1, + "defaultValue": 3, + "allowManualEntry": false, + "options": [ + { + "label": "Open (Up)", + "value": 1 + }, + { + "label": "Close (Down)", + "value": 2 + }, + { + "label": "Stop", + "value": 3 + } + ] + }, + { + "#": "6", + "label": "Calibrate Travel Point", + "valueSize": 1, + "defaultValue": 1, + "allowManualEntry": false, + "options": [ + { + "label": "Up travel point", + "value": 1 + }, + { + "label": "Down travel point", + "value": 2 + }, + { + "label": "Third travel point", + "value": 3 + } + ] + }, + { + "#": "7", + "label": "Delete Travel Point", + "valueSize": 1, + "defaultValue": 0, + "allowManualEntry": false, + "options": [ + { + "label": "Delete all travel", + "value": 0 + }, + { + "label": "Delete up travel", + "value": 1 + }, + { + "label": "Delete down travel", + "value": 2 + }, + { + "label": "Delete third travel", + "value": 3 + } + ] + }, + { + "#": "8", + "label": "Low Battery Level Alarm Threshold", + "valueSize": 1, + "unit": "%", + "minValue": 0, + "maxValue": 50, + "defaultValue": 10 + }, + { + "#": "9", + "label": "Battery Auto Report Interval Time", + "valueSize": 4, + "unit": "seconds", + "minValue": 0, + "maxValue": 2678400, + "defaultValue": 3600 + }, + { + "#": "10", + "label": "Battery Change Report Threshold", + "valueSize": 1, + "unit": "%", + "minValue": 0, + "maxValue": 50, + "defaultValue": 5 + }, + { + "#": "11", + "label": "Curtain Status Report Command Setting", + "valueSize": 1, + "defaultValue": 6, + "allowManualEntry": false, + "options": [ + { + "label": "Send nothing", + "value": 0 + }, + { + "label": "Send Basic Report", + "value": 1 + }, + { + "label": "Send Multilevel Switch Report", + "value": 2 + }, + { + "label": "Send Window Covering Report", + "value": 3 + }, + { + "label": "Send Basic Report and Multilevel Switch Report", + "value": 4 + }, + { + "label": "Send Basic Report and Window Covering Report", + "value": 5 + }, + { + "label": "Send Multilevel Switch Report and Window Covering Report", + "value": 6 + }, + { + "label": "Send Basic Report and Multilevel Switch Report and Window Covering Report", + "value": 7 + } + ] + } + ], + "metadata": { + "inclusion": "Follow the user guide of hub to enter inclusion mode.\nPress and hold the Config Button more than 6s, enter Include Mode", + "exclusion": "Follow the user guide of hub to enter exclusion mode.\nPress and hold the Config Button more than 6s, enter Exclusion Mode", + "reset": "Press and hold the Config Button more than 10s. The Curtain will reset itself to factory default by sending a Device Reset Locally Notification to gateway when the button is released.\nNote:Please use this procedure only when the network primary controller is missing or otherwise inoperable" + } +} diff --git a/packages/eslint-plugin/src/utils/wordsAndNames.ts b/packages/eslint-plugin/src/utils/wordsAndNames.ts index fc41fd975f38..099530ed4b2b 100644 --- a/packages/eslint-plugin/src/utils/wordsAndNames.ts +++ b/packages/eslint-plugin/src/utils/wordsAndNames.ts @@ -33,6 +33,7 @@ const ccAndCommandNames = combinations( "Scene Activation", "Meter", "Indicator", + "Window Covering", ], [ "Command Class", From 00c63605176b1542596d348a12eb5ef59ea05399 Mon Sep 17 00:00:00 2001 From: AlCalzone Date: Thu, 24 Oct 2024 23:28:26 +0200 Subject: [PATCH 10/16] fix: detect bootloader when short chunks are received (#7318) --- packages/serial/src/ZWaveSerialPortBase.ts | 4 +- .../serial/src/parsers/BootloaderParsers.ts | 7 ++- packages/testing/src/MockController.ts | 18 +++++++- .../test/driver/bootloaderDetection.test.ts | 45 +++++++++++++++++++ .../src/lib/test/integrationTestSuite.ts | 6 +++ 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 packages/zwave-js/src/lib/test/driver/bootloaderDetection.test.ts diff --git a/packages/serial/src/ZWaveSerialPortBase.ts b/packages/serial/src/ZWaveSerialPortBase.ts index 803b6e4465e9..0f445df97f77 100644 --- a/packages/serial/src/ZWaveSerialPortBase.ts +++ b/packages/serial/src/ZWaveSerialPortBase.ts @@ -158,7 +158,9 @@ export class ZWaveSerialPortBase extends PassThrough { if (this.mode == undefined) { // If we haven't figured out the startup mode yet, // inspect the chunk to see if it contains the bootloader preamble - const str = (data as Buffer).toString("ascii").trim(); + const str = (data as Buffer).toString("ascii") + // like .trim(), but including null bytes + .replaceAll(/^[\s\0]+|[\s\0]+$/g, ""); this.mode = str.startsWith(bootloaderMenuPreamble) ? ZWaveSerialMode.Bootloader : ZWaveSerialMode.SerialAPI; diff --git a/packages/serial/src/parsers/BootloaderParsers.ts b/packages/serial/src/parsers/BootloaderParsers.ts index 18836aa1f0a5..c97d2be2efe2 100644 --- a/packages/serial/src/parsers/BootloaderParsers.ts +++ b/packages/serial/src/parsers/BootloaderParsers.ts @@ -79,8 +79,9 @@ export class BootloaderScreenParser extends Transform { const screen = this.receiveBuffer.slice(0, nulCharIndex).trim(); this.receiveBuffer = this.receiveBuffer.slice(nulCharIndex + 1); - this.logger?.bootloaderScreen(screen); + if (screen === "") continue; + this.logger?.bootloaderScreen(screen); this.push(screen); } @@ -107,7 +108,9 @@ export class BootloaderScreenParser extends Transform { } } -export const bootloaderMenuPreamble = "Gecko Bootloader"; +// Sometimes the first chunk of the bootloader screen is relatively short, +// so we consider the following enough to detect the bootloader menu: +export const bootloaderMenuPreamble = "Gecko Boo"; const preambleRegex = /^Gecko Bootloader v(?\d+\.\d+\.\d+)/; const menuSuffix = "BL >"; const optionsRegex = /^(?\d+)\. (?