From b3aa4795157bcffca53c4ad727650d6e9b92bc93 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Fri, 17 Jul 2026 09:04:39 +0200 Subject: [PATCH 1/5] mode-button: resolve nested conditions --- mode-button.rsc | 52 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/mode-button.rsc b/mode-button.rsc index f46eaa3c..d7a0b3f1 100644 --- a/mode-button.rsc +++ b/mode-button.rsc @@ -54,32 +54,34 @@ :set ModeButtonScheduler; /system/scheduler/remove [ find where name="_ModeButtonScheduler" ]; - :if ([ :len $Code ] > 0) do={ - :if ([ $ValidateSyntax $Code ] = true) do={ - $LogPrint info $FuncName ("Acting on " . $Count . " mode-button presses: " . $Code); - - :for I from=1 to=$Count do={ - $LEDInvert; - :if ([ /system/routerboard/settings/get silent-boot ] = false) do={ - :beep length=200ms; - } - :delay 200ms; - $LEDInvert; - :delay 200ms; - } - - :onerror Err { - [ :parse $Code ]; - } do={ - $LogPrint warning $FuncName \ - ("The code for " . $Count . " mode-button presses failed with runtime error: " . $Err); - } - } else={ - $LogPrint warning $FuncName \ - ("The code for " . $Count . " mode-button presses failed syntax validation!"); - } - } else={ + :if ([ :len $Code ] = 0) do={ $LogPrint info $FuncName ("No action defined for " . $Count . " mode-button presses."); + :return false; + } + + :if ([ $ValidateSyntax $Code ] = false) do={ + $LogPrint warning $FuncName \ + ("The code for " . $Count . " mode-button presses failed syntax validation!"); + :return false; + } + + $LogPrint info $FuncName ("Acting on " . $Count . " mode-button presses: " . $Code); + + :for I from=1 to=$Count do={ + $LEDInvert; + :if ([ /system/routerboard/settings/get silent-boot ] = false) do={ + :beep length=200ms; + } + :delay 200ms; + $LEDInvert; + :delay 200ms; + } + + :onerror Err { + [ :parse $Code ]; + } do={ + $LogPrint warning $FuncName \ + ("The code for " . $Count . " mode-button presses failed with runtime error: " . $Err); } } do={ :global ExitOnError; $ExitOnError $0 $Err; From 0e5a30d946039bfb9f7532c59986e5529c17821a Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Fri, 17 Jul 2026 09:16:56 +0200 Subject: [PATCH 2/5] mode-button: spilt the into two scripts --- doc/mode-button.md | 6 +-- mode-button-scheduler.rsc | 78 +++++++++++++++++++++++++++++++++++++++ mode-button.rsc | 71 +++-------------------------------- 3 files changed, 86 insertions(+), 69 deletions(-) create mode 100644 mode-button-scheduler.rsc diff --git a/doc/mode-button.md b/doc/mode-button.md index 2ac3149b..547396d5 100644 --- a/doc/mode-button.md +++ b/doc/mode-button.md @@ -16,7 +16,7 @@ Mode button with multiple presses Description ----------- -This script extend the functionality of mode button. Instead of just one +These scripts extend the functionality of mode button. Instead of just one you can trigger several actions by pressing the mode button several times. The hardware needs to have a mode button, see @@ -39,9 +39,9 @@ Copy this code to terminal to check: Requirements and installation ----------------------------- -Just install the script: +Just install the scripts: - $ScriptInstallUpdate mode-button; + $ScriptInstallUpdate mode-button,mode-button-scheduler; Then configure the mode button to run `mode-button`: diff --git a/mode-button-scheduler.rsc b/mode-button-scheduler.rsc new file mode 100644 index 00000000..921cee22 --- /dev/null +++ b/mode-button-scheduler.rsc @@ -0,0 +1,78 @@ +#!rsc by RouterOS +# RouterOS script: mode-button-scheduler +# Copyright (c) 2018-2026 Christian Hesse +# https://rsc.eworm.de/COPYING.md +# +# requires RouterOS, version=7.21 +# requires device-mode, scheduler +# +# act on multiple mode and reset button presses +# https://rsc.eworm.de/doc/mode-button.md + +:local ExitOK false; +:onerror Err { + :global GlobalConfigReady; :global GlobalFunctionsReady; + :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ + do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; + :local ScriptName [ :jobname ]; + + :global ModeButton; + + :global LogPrint; + :global ModeButtonScheduler; + :global ValidateSyntax; + + :local LEDInvert do={ + :global ModeButtonLED; + + :global IfThenElse; + + :local LED [ /system/leds/find where leds=$ModeButtonLED \ + !disabled type~"^(on|off)\$" interface=[] ]; + :if ([ :len $LED ] = 0) do={ + :return false; + } + /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED; + } + + :local Count ($ModeButton->"count"); + :local Code ($ModeButton->[ :tostr $Count ]); + + :set ($ModeButton->"count") 0; + :set ModeButtonScheduler; + /system/scheduler/remove [ find where name="mode-button-scheduler" ]; + + :if ([ :len $Code ] = 0) do={ + $LogPrint info $ScriptName ("No action defined for " . $Count . " mode-button presses."); + :set ExitOK true; + :error false; + } + + :if ([ $ValidateSyntax $Code ] = false) do={ + $LogPrint warning $ScriptName \ + ("The code for " . $Count . " mode-button presses failed syntax validation!"); + :set ExitOK true; + :error false; + } + + $LogPrint info $ScriptName ("Acting on " . $Count . " mode-button presses: " . $Code); + + :for I from=1 to=$Count do={ + $LEDInvert; + :if ([ /system/routerboard/settings/get silent-boot ] = false) do={ + :beep length=200ms; + } + :delay 200ms; + $LEDInvert; + :delay 200ms; + } + + :onerror Err { + [ :parse $Code ]; + } do={ + $LogPrint warning $ScriptName \ + ("The code for " . $Count . " mode-button presses failed with runtime error: " . $Err); + } +} do={ + :global ExitError; $ExitError $ExitOK [ :jobname ] $Err; +} diff --git a/mode-button.rsc b/mode-button.rsc index d7a0b3f1..fb70101b 100644 --- a/mode-button.rsc +++ b/mode-button.rsc @@ -21,75 +21,14 @@ :set ($ModeButton->"count") ($ModeButton->"count" + 1); - :local Scheduler [ /system/scheduler/find where name="_ModeButtonScheduler" ]; + :local Scheduler [ /system/scheduler/find where name="mode-button-scheduler" ]; :if ([ :len $Scheduler ] = 0) do={ - $LogPrint info $ScriptName ("Creating scheduler _ModeButtonScheduler, counting presses..."); - :global ModeButtonScheduler do={ :onerror Err { - :local FuncName $0; - - :global ModeButton; - - :global LogPrint; - :global ModeButtonScheduler; - :global ValidateSyntax; - - :local LEDInvert do={ - :global ModeButtonLED; - - :global IfThenElse; - - :local LED [ /system/leds/find where leds=$ModeButtonLED \ - !disabled type~"^(on|off)\$" interface=[] ]; - :if ([ :len $LED ] = 0) do={ - :return false; - } - /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED; - } - - :local Count ($ModeButton->"count"); - :local Code ($ModeButton->[ :tostr $Count ]); - - :set ($ModeButton->"count") 0; - :set ModeButtonScheduler; - /system/scheduler/remove [ find where name="_ModeButtonScheduler" ]; - - :if ([ :len $Code ] = 0) do={ - $LogPrint info $FuncName ("No action defined for " . $Count . " mode-button presses."); - :return false; - } - - :if ([ $ValidateSyntax $Code ] = false) do={ - $LogPrint warning $FuncName \ - ("The code for " . $Count . " mode-button presses failed syntax validation!"); - :return false; - } - - $LogPrint info $FuncName ("Acting on " . $Count . " mode-button presses: " . $Code); - - :for I from=1 to=$Count do={ - $LEDInvert; - :if ([ /system/routerboard/settings/get silent-boot ] = false) do={ - :beep length=200ms; - } - :delay 200ms; - $LEDInvert; - :delay 200ms; - } - - :onerror Err { - [ :parse $Code ]; - } do={ - $LogPrint warning $FuncName \ - ("The code for " . $Count . " mode-button presses failed with runtime error: " . $Err); - } - } do={ - :global ExitOnError; $ExitOnError $0 $Err; - } } - /system/scheduler/add name="_ModeButtonScheduler" \ - on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s; + $LogPrint info $ScriptName ("Creating scheduler mode-button-scheduler, counting presses..."); + /system/scheduler/add name="mode-button-scheduler" interval=3s \ + on-event="/system/script/run mode-button-scheduler;"; } else={ - $LogPrint debug $ScriptName ("Updating scheduler _ModeButtonScheduler..."); + $LogPrint debug $ScriptName ("Updating scheduler mode-button-scheduler..."); /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ]; } } do={ From 198b088faaeec4d63ea04c7705b0a778fb6b653c Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Fri, 17 Jul 2026 09:21:50 +0200 Subject: [PATCH 3/5] mode-button{,-scheduler}: store to and get count from scheduler's comment --- mode-button-scheduler.rsc | 14 ++++++++++---- mode-button.rsc | 9 ++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mode-button-scheduler.rsc b/mode-button-scheduler.rsc index 921cee22..a7bedfb6 100644 --- a/mode-button-scheduler.rsc +++ b/mode-button-scheduler.rsc @@ -35,12 +35,18 @@ /system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED; } - :local Count ($ModeButton->"count"); + :local Scheduler [ /system/scheduler/find where name="mode-button-scheduler" ]; + + :if ([ :len $Scheduler ] = 0) do={ + $LogPrint error $ScriptName ("Scheduler does not exist."); + :set ExitOK true; + :error false; + } + + :local Count ([ :deserialize from=json [ /system/scheduler/get $Scheduler comment ] ]->"count"); :local Code ($ModeButton->[ :tostr $Count ]); - :set ($ModeButton->"count") 0; - :set ModeButtonScheduler; - /system/scheduler/remove [ find where name="mode-button-scheduler" ]; + /system/scheduler/remove $Scheduler; :if ([ :len $Code ] = 0) do={ $LogPrint info $ScriptName ("No action defined for " . $Count . " mode-button presses."); diff --git a/mode-button.rsc b/mode-button.rsc index fb70101b..6210ac97 100644 --- a/mode-button.rsc +++ b/mode-button.rsc @@ -15,21 +15,20 @@ do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; - :global ModeButton; - :global LogPrint; - :set ($ModeButton->"count") ($ModeButton->"count" + 1); - :local Scheduler [ /system/scheduler/find where name="mode-button-scheduler" ]; :if ([ :len $Scheduler ] = 0) do={ $LogPrint info $ScriptName ("Creating scheduler mode-button-scheduler, counting presses..."); /system/scheduler/add name="mode-button-scheduler" interval=3s \ + comment=[ :serialize to=json ({ count=1 }) ] \ on-event="/system/script/run mode-button-scheduler;"; } else={ $LogPrint debug $ScriptName ("Updating scheduler mode-button-scheduler..."); - /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ]; + :local Presses (([ :deserialize from=json [ /system/scheduler/get $Scheduler comment ] ]->"count") + 1); + /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ] \ + comment=[ :serialize to=json ({ count=$Presses }) ]; } } do={ :global ExitOnError; $ExitOnError [ :jobname ] $Err; From 68dfe26198d2b433977f65460f8f2c2d347d305a Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Fri, 17 Jul 2026 09:24:47 +0200 Subject: [PATCH 4/5] mode-button: drop all global variables With RouterOS 7.24 (actually 7.24beta1 I think) the mode, reset and wps button scripts are run with limited policy. This causes global variables not being available... So let's drop them here. This is kind of inconvenient, but we can live with it. The second script 'mode-button-scheduler' is run from scheduler and does not suffer that limitation. --- mode-button.rsc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/mode-button.rsc b/mode-button.rsc index 6210ac97..364fa33f 100644 --- a/mode-button.rsc +++ b/mode-button.rsc @@ -10,26 +10,21 @@ # https://rsc.eworm.de/doc/mode-button.md :onerror Err { - :global GlobalConfigReady; :global GlobalFunctionsReady; - :retry { :if ($GlobalConfigReady != true || $GlobalFunctionsReady != true) \ - do={ :error ("Global config and/or functions not ready."); }; } delay=500ms max=50; :local ScriptName [ :jobname ]; - :global LogPrint; - :local Scheduler [ /system/scheduler/find where name="mode-button-scheduler" ]; :if ([ :len $Scheduler ] = 0) do={ - $LogPrint info $ScriptName ("Creating scheduler mode-button-scheduler, counting presses..."); + :log info ($ScriptName . ": Creating scheduler mode-button-scheduler, counting presses..."); /system/scheduler/add name="mode-button-scheduler" interval=3s \ comment=[ :serialize to=json ({ count=1 }) ] \ on-event="/system/script/run mode-button-scheduler;"; } else={ - $LogPrint debug $ScriptName ("Updating scheduler mode-button-scheduler..."); + :log debug ($ScriptName . ": Updating scheduler mode-button-scheduler..."); :local Presses (([ :deserialize from=json [ /system/scheduler/get $Scheduler comment ] ]->"count") + 1); /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ] \ comment=[ :serialize to=json ({ count=$Presses }) ]; } } do={ - :global ExitOnError; $ExitOnError [ :jobname ] $Err; + :log error ([ :jobname ] . ": " . $Err); } From 16cfe335c2ab416ea1acd83a6b1cd645e7e12bb4 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Fri, 17 Jul 2026 09:33:48 +0200 Subject: [PATCH 5/5] announce the change on mode-button, run migration --- global-functions.rsc | 2 +- news-and-changes.rsc | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/global-functions.rsc b/global-functions.rsc index 8685346f..85744878 100644 --- a/global-functions.rsc +++ b/global-functions.rsc @@ -15,7 +15,7 @@ # Git commit id & info, expected configuration version :global CommitId "unknown"; :global CommitInfo "unknown"; -:global ExpectedConfigVersion 143; +:global ExpectedConfigVersion 144; # global variables not to be changed by user :global GlobalFunctionsReady false; diff --git a/news-and-changes.rsc b/news-and-changes.rsc index ed60a6c2..1b3adb26 100644 --- a/news-and-changes.rsc +++ b/news-and-changes.rsc @@ -68,6 +68,7 @@ 141="Introduced script 'dhcpv6-client-lease' to run several scripts on IPv6 DHCP client lease."; 142="Added a setting for 'mod/notification-email' to check availability of certificate chain."; 143="Made backup scripts 'backup-email' and 'backup-upload' support date & time in filenames."; + 144="Split and reworked 'mode-button' for compatibility with RouterOS 7.24, configuration was updated automatically."; }; # Migration steps to be applied on script updates @@ -79,4 +80,5 @@ 132=":if ([ :len [ /system/script/find where name=\"check-health\" ] ] > 0) do={ :local Code \":local Install \\\"check-health\\\"; :if ([ :len [ /system/health/find where type=\\\"\\\" name~\\\"-state\\\\\\\$\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/state\\\"); }; :if ([ :len [ /system/health/find where type=\\\"C\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/temperature\\\"); }; :if ([ :len [ /system/health/find where type=\\\"V\\\" ] ] > 0) do={ :set Install (\\\$Install . \\\",check-health.d/voltage\\\"); }; :global ScriptInstallUpdate; \\\$ScriptInstallUpdate \\\$Install;\"; :global ValidateSyntax; :if ([ \$ValidateSyntax \$Code ] = true) do={ :do { [ :parse \$Code ]; } on-error={ }; }; };"; 138="/certificate/set trusted=yes [ find where trusted=yes ];"; 140=":if ([ :len [ /system/script/find where name=\"lease-script\" ] ] > 0) do={ /system/script/set name=\"dhcpv4-server-lease\" \"lease-script\"; :global ScriptInstallUpdate; \$ScriptInstallUpdate; /ip/dhcp-server/set lease-script=\"dhcpv4-server-lease\" [ find where lease-script=\"lease-script\" ]; };"; + 144=":if ([ :len [ /system/script/find where name=\"mode-button\" ] ] > 0) do={ :global ScriptInstallUpdate; \$ScriptInstallUpdate mode-button-scheduler; };"; };