├── README.md
├── _ui_test.cfg
├── _user_interaction.cfg
└── pics
└── MacroTitles&Comments.png
/README.md:
--------------------------------------------------------------------------------
1 | # Klipper_UserInteraction
2 | A set of macros to enable print-time user interaction with Klipper via Console and UI buttons (macros).
3 | Copyright (C) 2022-2023 Tod A. Wulff
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, either version 3 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU General Public License for more details.
14 |
15 | Should you have a need to receive a copy of the GNU General Public License,
16 | see .
17 |
18 | 27Apr23 - Adopted GNU GPLv3 for these works. Added example of using UI to go interactive with user
19 | when host system cpu utilization exceeds some programmatic value at print start.
20 |
21 | 04APR23 Update: Added example of using UI Interaction during an Idle Timeout Event.
22 |
23 | 19MAR23 Update: Added Text Decoration to the queries. Updated code herein with the latest from my
24 | production printer environment. Added verbal hinting/reminding with TTS macro calls: say & say_wait.
25 |
26 | Macro list/comments:
27 | 
28 |
29 | Video of an early implementation: https://youtu.be/pgBfhVAYsHU
30 |
31 | Note that the files herein may not be the latest - 'production' code is attainable via my printer config:
32 | https://github.com/TodWulff/V2.2526_Config/blob/main/_user_interaction.cfg and
33 | https://github.com/TodWulff/V2.2526_Config/blob/main/_ui_test.cfg
34 |
35 | Here is an example of a real world use case - optionally keeping heaters on & unloading filament @ print end:
36 | 
37 |
38 | Here is another example of a real world use case - optionally rebooting after an ERCF calibration event:
39 | 
40 | 
41 |
42 | Other cases in which I employ this module in my daily printing workflow:
43 |
44 | - During an Idle Timeout event, give user ability to cancel the shutdown, to proceed, and to upload configs and then proceed,
45 | with null-input timeout to default to proceed with the shutdown (as imaged).
46 |
47 | 
48 |
49 | - At print start, if system cpu utilization exceeds a threshold, go interactive to give user an opportunity to resolve the issue (wait for the host to settle down (i.e. if transcoding a timelapse from a prior print), or to change the threshold value via the console).
50 |
51 | 
52 |
53 | - At print start, if extrusion factor or speed factor is not 100%, go interactive to give user option to reset each to 100% before commencing the print:
54 |
55 | 
56 |
57 | - At print canx/end, give user opportunity to retain heater settings, or to turn heaters off;
58 | - At print canx/end, give user option to retain or unload filament (12-color Multi-Material ERCF here);
59 | - At print end, give user opportunity to push current configs up to github repo, for historization and disaster recovery purposes:
60 | - The '_git_repo_ops' macro set has been enhanced to query the user for a 1-72 character commit summary message
61 |
62 | 
63 |
64 | - Using UI to enable SomaFM Web Streamed Radio channel selection by displaying an index of channel names/numbers and querying the user to make a selection:
65 |
66 | 
67 |
68 | - During calibration of ERCF Filament Encoder, for each color/cart, give user ability to restart the calibration or to accept same and continue
69 | - Used UI module during testing of various hardware items (i.e. servo throw angle determinations for v0.1 nozzle scrubber and side swipe klicky/euclid bed probes)
70 | - ... more that I cannot recall right now
71 |
72 |
73 | ## USER-SUBMITTED USE CASES:
74 |
75 | Here is a user-submitted example of using this macro library to implement an interactive M600 Filament Change process:
76 | https://gist.github.com/Beatzoid/b66cad5ed74f2ad23529857bcc45636c
77 | Thanks to Discord User Beatzoid#8010 for providing this fine example.
78 |
79 |
80 | ## PREREQUISITES:
81 |
82 | Heavily relies on `[Save_Variables]` module
83 | see: https://github.com/TodWulff/V2.2526_Config/blob/main/_persistent_variables.cfg
84 |
85 | Makes use of **`M300`** and some custom **`M300_`** related macros I mucked with for emitting sounds, so if you're not so
86 | interested, or haven't a beeper on your box, comment out related lines: `_ui_reminder`, `_annunciate_input_exception`,
87 | and `_annunciate_good_input` - see: https://github.com/TodWulff/V2.2526_Config/blob/main/_m300_sounds.cfg
88 |
89 | Makes heavy use of the **`[response]`** module - I've trapped the stock M118 (using rename_existing) with gcode such
90 | that it uses action_notification vs. FW M118 code. This enables emission of 'special characters' that M118's
91 | FW code chokes on - see: https://github.com/TodWulff/V2.2526_Config/blob/main/_gcode_macros.cfg
92 |
93 | Throughout my configs, virtually all gcode macros have been 'instrumented' - the first and last lines of each gcode block can be deleted.
94 | I have these as I have an ability to trace macro code execution and display same in the console - quite powerful for macro development/
95 | troubleshooting but of little/no use to others, with rare exception -
96 | see: https://github.com/TodWulff/V2.2526_Config/blob/main/_debug_trace.cfg
97 |
98 | Users should consider adding a User Input macro pane, as depicted below - which calls macros herein.
99 | Orienting it under the console pane will allow it to become intuitive with a small bit of use. Author's UI of choice is Mainsail.
100 | For Fluidd or other Moonraker Clients (Mooncord/Telegram Bot/...), I defer to others to adapt the macros for use therein.
101 |
102 | 
103 |
104 | ## Primary Macro: GET_USER_INPUT Parameters and related dialog follows
105 |
106 | Optional Parameters:
107 |
108 | **`PROMPT`** Quoted Text to display in console as a user input prompt. Promts are vistually separated from balance
109 | of console context via a dashed line being displayed before and after the prompt (see images below).
110 |
111 | **`RCVR_MACRO`** Macro name to run when VALID input received - UI_INPUT param, containing user input contents is
112 | passed to the called macro - if required, the called user macro can query svv for additional details.
113 |
114 | **`TYPE`** The TYPE of input needed - one of these three string/str, integer/int, float/flt
115 |
116 | **`BOUNDS_HI`** if TYPE is float/flt, input must be >=lo and <=hi - for integer/int, input must be >=floor(lo) and <=ceiling(hi)
117 |
118 | **`BOUNDS_LO`** - if string/str TYPE, character count must be >=floor(lo) and <=ceiling(hi)
119 |
120 | **`TO_PERIOD`** Period in Integer seconds to wait for user input
121 |
122 | **`RMDR_PERIOD`** Overrides the default reminder period set in \_ui_vars while waiting for user input
123 |
124 | **`EXCPT_HDLR`** Macro name is called in the event of an input timeout or faulty input - no params passed - query svv...
125 |
126 | **`TO_CYCL_DEF`** iterations of timeouts before TO_RESP_DEF is sent as UI_INPUT to RCVR_MACRO - default: -1 to disable behavior
127 |
128 | **`TO_RESP_DEF`** UI_INPUT value to be passed if TO_CYCL_DEF count reaches 0 when decremented @ timeout
129 |
130 | For string TYPE, if **`BOUNDS_LO`** is not asserted, defaults to 1.
131 |
132 | For string TYPE, if **`BOUNDS_HI`** is not asserted, defaults to 255.
133 |
134 | For float TYPE, if **`BOUNDS_LO`** is not asserted, defaults to -999999999.0.
135 |
136 | For float TYPE, if **`BOUNDS_HI`** is not asserted, defaults to 999999999.0.
137 |
138 | For integer TYPE, if **`BOUNDS_LO`** is not asserted, defaults to -999999999.
139 |
140 | For integer TYPE, if **`BOUNDS_HI`** is not asserted, defaults to 999999999
141 |
142 | If additional bounds testing is desired/required, it's up to the user implementing this on their printers to craft more granular
143 | test and validation - by way of the **`RCVR_MACRO`**. If I've blantantly missed something, then let me know. :)
144 |
145 | For optional parameters, read the code to understand implications of relying on defaults.
146 |
147 | Again, ALL parameters are optional and default to something (depicted in the following):
148 |
149 | PROMPT="Awaiting User Input:" displayed on the console at macro start as a user prompt
150 | TYPE=STRING 'string'('str') or 'integer'('int') or 'float'('flt') - for buttons use string
151 | BOUNDS_LO=1 min string chars or min numercial value (Int/Flt -999999999)
152 | BOUNDS_HI=255 max string chars or max numercial value (Int/Flt 999999999)
153 | RCVR_MACRO="_test_show_user_input" to accept param UI_INPUT that will be an int/flt/"string" that was
154 | input and passes sniff test (simple bounding checks)
155 | TO_PERIOD=120 in seconds
156 | EXCPT_HDLR="_ui_exception_handler" no params passed to this proc - use svv to get runtime specifics if needed
157 | TO_CYCL_DEF=-1
158 | TO_RESP_DEF="NULL" if TO_CYCLES >=0, this param will be passed to RCVR_MACRO via UI_INPUT if no user input received
159 | RMDR_PERIOD=15 reminder bleeps every n seconds - 0 will disable reminder beeps
160 |
161 | Also, there are four other module macro variables that affect the function of the macros
162 |
163 | variable_ui_input_check_recurse_period: 0.5 # seconds - period between checking for input when expected - 0.5s is good - less leads to more host
164 | # cycle consumption, larger leads to reduced perceived responsiveness
165 | variable_ui_reminder_enable: 1 # bool 1/0 - 0 overtly disables reminder bleeps regardless of RMDR_PERIOD param
166 | variable_ui_enable_input_hints: 0 # bool 1/0 - defaults to disabling hints on input prompt
167 | variable_ui_disable_exception_hints: 0 # bool 1/0 - defaults to enabling hints on an exception event
168 |
169 | These can be programmatically altered during runtime via use of SET_GCODE_VARIABLE gcode command - i.e.:
170 |
171 | SET_GCODE_VARIABLE MACRO=_ui_vars VARIABLE=ui_enable_input_hints VALUE=1
172 |
173 | ### Example call follows:
174 | This call looks for a user to enter a string 1-12 chars long, with a timeout of 60 secs, that forwards (via UI_INPUT param),
175 | the entered string to the '_test_show_user_input' macro (default if no RCVR_MACRO passed by user call)
176 | If a timeout happens/faulty input is detected, the _ui_timeout_watchdog/_validate_user_input macros call '_ui_exception_handler' macro
177 | (which is the default exception handler if no EXCPT_HDLR macro name is passed by the user call)
178 |
179 | `get_user_input` `PROMPT="Enter/click something:"` `TYPE=string` `BOUNDS_LO=1` `BOUNDS_HI=12` `RCVR_MACRO=_test_show_user_input` `TO_PERIOD=60` `EXCPT_HDLR=_ui_exception_handler`
180 |
181 | ## EXCEPTION HANDLER MACRO:
182 | if a custom **`EXCPT_HDLR`** macro is to be instantiated, it may prove useful to consider the following:
183 | - **`GET_USER_INPUT`** does the following:
184 | - initializes states and then displays the user **`PROMPT`**
185 | - sets the timeout watchdog to fire after the asserted (120s default) **`TO_PERIOD`**
186 | - puts **`_await_user_input`** into a recursive loop, non-blocking while waiting, with a period defined in _ui_vars
187 | - a single **`EXCPT_HDLR`** macro services both bad input cases as well as the time-out when waiting on user input.
188 | - for timeouts the **default** **`EXCPT_HDLR`** macro simply recalls **`GET_USER_INPUT`** giving user another input context
189 | - this approach can be altered with a custom **`EXCPT_HDLR`** macro being passed to the **`GET_USER_INPUT`** call
190 | - When `_await_user_input` detects input from user, `_validate_user_input` tests for **`TYPE`** and **`BOUNDS_HI`**/**`BOUNDS_LO`** compliance
191 | - if input is NOT **`TYPE`** & **`BOUNDS_HI`**/**`BOUNDS_LO`** compliant, `_await_user_input` recalls **`GET_USER_INPUT`** so user can fix it
192 | - if input IS **`TYPE`** & **`BOUNDS_HI`**/**`BOUNDS_LO`** compliant (flag `_ui_bad_input` NOT set), input is sent to **`RCVR_MACRO`** via **`UI_INPUT`** param
193 |
194 | In most conceivable use cases, the default exception handler/validation macros should be adequate. However, author decided to give others
195 | options, in the event something wasn't adequately considered. Either an entirely new **`EXCPT_HDLR`** can be crafted or, as demonstrated
196 | in _ui_test.cfg's `_ui_test_exception_handler` (custom **`EXCPT_HDLR`**) code runs and then chains to the stock `_ui_exception_handler` below
197 |
198 | In `_ui_vars` macro, a person implementing this can selectively enable/disable Input Prompt and/or Exception hints. Hints are
199 | little descriptive blurbs as to what the macro is expecting as input - one can enable hints on either the input prompt,
200 | or disable hints when an input exception is detected/raised. It is suggested that it is likely best to have exception
201 | hints enabled, and to have the input prompt detail what sort of input is desired, leaving input hints disabled.
202 | The `_ui_test.cfg` file has the START_DEMO macro that iterates through some UI input event - I used it for dev testing,
203 | it works as a demo.
204 |
205 | ## Some visual examples as it relates to the hint options that can be set in `_ui_vars`:
206 |
207 | No Hints on Exception nor Input Prompt: https://i.imgur.com/PDPOmTJ.png (likely too terse - user should make the preamble/prompt detailed)
208 |
209 | 
210 |
211 | Hints on Exception only, not on Input Prompt: https://i.imgur.com/D5Ih6hE.png (the author's preference)
212 |
213 | 
214 |
215 | Hints on Input Prompt but not on an Exception: https://i.imgur.com/Deo2SNr.png (leads to some noise, which may be tolerable)
216 |
217 | 
218 |
219 | Hints on both Input Prompt and when an exception is raised: https://i.imgur.com/mO7TfWW.png (likely redundant, imo)
220 |
221 | 
222 |
223 | ## Closing comments:
224 | This was/is a quite deep rabbit hole. Author `MegaHurtz 🇺🇸#6544` can be reached on a number of different Discord servers:
225 | - DIY 3D Printer Kit Feedback
226 | - Voron Design
227 | - Klipper
228 | - Mainsail
229 | - ...
230 |
231 | Don't hesitate to reach out as may be needed. Have a great day. Happy Printing!
232 |
233 | ~MHz
234 |
--------------------------------------------------------------------------------
/_ui_test.cfg:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------
2 | # _user_interaction (UI) module: test macros
3 | # Copyright (C) 2022-2023 Tod A. Wulff
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # Should you have a need to receive a copy of the GNU General Public License,
16 | # see .
17 | #--------------------------------------------------------------------
18 |
19 | [delayed_gcode _ui_test_loaded]
20 | initial_duration: 4.501
21 | gcode:
22 | _proc_start function=_ui_test_loaded func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
23 | _module_loaded MODULE=_ui_test
24 | _proc_end function=_ui_test_loaded
25 |
26 | [delayed_gcode _ui_test_module_start]
27 | #description: Initialization
28 | initial_duration: 0.75 # have this at 0.75s as init code in _startup_autoexec.cfg runs at 0.1s after start
29 | gcode:
30 |
31 | _proc_start function=_ui_test_module_start func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
32 |
33 | _ui_clear_test_cache
34 |
35 | _proc_end function=_ui_test_module_start
36 |
37 | #--------------------------------------------------------------------
38 |
39 | [gcode_macro _ui_clear_test_cache]
40 | description: helper proc to wipe/initialize svv contents related to user input
41 | gcode:
42 |
43 | _proc_start function=_ui_clear_test_cache func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
44 |
45 | SAVE_VARIABLE VARIABLE=ui_test_day_name VALUE='""'
46 | SAVE_VARIABLE VARIABLE=ui_test_day_num VALUE=0
47 | SAVE_VARIABLE VARIABLE=ui_test_float VALUE=-9999999.99
48 | SAVE_VARIABLE VARIABLE=ui_test_mo_name VALUE='""'
49 | SAVE_VARIABLE VARIABLE=ui_test_mo_num VALUE=-9999999
50 | SAVE_VARIABLE VARIABLE=ui_test_year VALUE=-9999999
51 |
52 | _proc_end function=_ui_clear_test_cache
53 |
54 | #--------------------------------------------------------------------
55 |
56 | [gcode_macro _ui_test_exception_handler]
57 | description: demo use of an exception handler
58 | gcode:
59 |
60 | _proc_start function=_ui_test_exception_handler func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
61 |
62 | M118 Welp, you weren't quick enough or made an entry error
63 | M118 note the hints provide not only what is being asked for
64 | M118 but also how long the macro is parameterized to wait for user input...
65 | M118 This is a customized input exception handler for timeout and errorneous input.
66 | M118 The default handler is what you will observe now.
67 | M118 -------------------------------
68 |
69 | _ui_exception_handler
70 |
71 | _proc_end function=_ui_test_exception_handler
72 |
73 | #--------------------------------------------------------------------
74 |
75 | [gcode_macro _start_ui_test]
76 | description: for demo'g/testing the user interaction 'module'
77 | gcode:
78 |
79 | _proc_start function=_start_ui_test func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
80 |
81 | _ui_clear_test_cache
82 |
83 | M118 Good Day, Mate! The User Interaction (UI) demo/test is starting:
84 | M118 The User Interaction ('UI' going forward - not to be confused with User Interface) has
85 | M118 input type and value bounding features, timeout detection, and dynamic forwarding of
86 | M118 validated input to 'Receiver Macros' enabling interactivity and helping to ensure that
87 | M118 TYPE of input matches what was requested and is needed by the receiver macro.
88 |
89 | # note that when no bounds are asserted in the call, string lengths are bound from 1 to 255 characters as hinted to
90 | get_user_input prompt="Press any of the UI buttons below to continue:" TYPE=str RCVR_MACRO=_starta_ui_test EXCPT_HDLR=_ui_test_exception_handler
91 |
92 | _proc_end function=_start_ui_test
93 |
94 | #--------------------------------------------------------------------
95 |
96 | [gcode_macro _starta_ui_test]
97 | description: for demo'g/testing the user interaction 'module'
98 | gcode:
99 |
100 | _proc_start function=_starta_ui_test func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
101 |
102 | M118 There ya go - You clicked/entered: {params.UI_INPUT}
103 |
104 | M118 This series of test macros serve to demonstrate these features.
105 | M118 Timeouts can be assigned from 0 to ~infinity seconds (defaults to 120 seconds).
106 | M118 This particular test step has an intentionally short timeout assigned (15s).
107 | M118 This is so user can observe a timeout without having to wait for 2 minutes...
108 |
109 | get_user_input prompt="Press any of the UI buttons below to continue:" TYPE=str RCVR_MACRO=_startb_ui_test EXCPT_HDLR=_ui_test_exception_handler TO_PERIOD=15
110 |
111 | _proc_end function=_starta_ui_test
112 |
113 | #--------------------------------------------------------------------
114 |
115 | [gcode_macro _startb_ui_test]
116 | description: for demo'g/testing the user interaction 'module'
117 | gcode:
118 |
119 | _proc_start function=_startb_ui_test func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
120 |
121 | M118 Oh Yeah.!. You clicked/entered: {params.UI_INPUT}
122 |
123 | M118 For an aid in understanding things a bit better, it might be useful to 'tail' the saved vars file
124 | M118 so that you can see what data is stored and changes as user interaction takes place. You can do
125 | M118 with a command such as: tail -f -n 30 /home/pi/printer_data/config/your_saved_vars_file.cfg
126 | M118 in a ssh terminal window.
127 |
128 | get_user_input prompt="Press any of the UI buttons below to continue:" TYPE=str RCVR_MACRO=_startc_ui_test EXCPT_HDLR=_ui_test_exception_handler
129 |
130 | _proc_end function=_startb_ui_test
131 |
132 | #--------------------------------------------------------------------
133 |
134 | [gcode_macro _startc_ui_test]
135 | description: for demo'g/testing the user interaction 'module'
136 | gcode:
137 |
138 | _proc_start function=_startc_ui_test func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
139 |
140 | M118 Schuite. You clicked/entered: {params.UI_INPUT}
141 |
142 | M118 The UI module provides hints on the input needed, how quickly it needs to be responded to,
143 | M118 and also provides a gentle reminder nudge every 30 seconds (customizable period) while waiting
144 | M118 for user input.
145 |
146 | get_user_input prompt="Press any of the UI buttons below to continue:" TYPE=str RCVR_MACRO=_startd_ui_test EXCPT_HDLR=_ui_test_exception_handler
147 |
148 | _proc_end function=_startc_ui_test
149 |
150 | #--------------------------------------------------------------------
151 |
152 | [gcode_macro _startd_ui_test]
153 | description: for demo'g/testing the user interaction 'module'
154 | gcode:
155 |
156 | _proc_start function=_startd_ui_test func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
157 |
158 | M118 Kudos. You clicked/entered: {params.UI_INPUT}
159 |
160 | M118 For testing, you can let the input timeout and observe the reminder firing and also what happens
161 | M118 on a timeout, and use the USER ENTRY button to enter an integer/float vs. hitting one of the buttons
162 | M118 to see what the response is to an invalid input TYPE.
163 | M118 On this first interaction, the user is requested to enter an integer year within 30 seconds.
164 |
165 | get_user_input prompt="Press any of the UI buttons below to continue:" TYPE=str RCVR_MACRO=_ui_test_get_year EXCPT_HDLR=_ui_test_exception_handler
166 |
167 | _proc_end function=_startd_ui_test
168 |
169 | #--------------------------------------------------------------------
170 |
171 | [gcode_macro _ui_test_get_year]
172 | description: first test proc, to get integer year
173 | # note that the proc that is chained to gets passes the previous input in parameter UI_INPUT
174 | # further, the saved variables has additional information which is progammatically exposed
175 | gcode:
176 |
177 | _proc_start function=_ui_test_get_year func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
178 |
179 | M118 Very good. You clicked/entered: {params.UI_INPUT}
180 | M118 Next we're going to be asking for the year. It is programmatically bound to the years 1900 to 2500.
181 | M118 Enter an integer, using the USER ENTRY button, that falls outside this range to test integer bounds
182 | M118 checking and validation. You should note the hints provided and also the default timeout and/or
183 | M118 input exception script functionality. On this test step, other than RCVR_MACRO macro, TYPE, and bound
184 | M118 limits assignment, all other parameters of get_user_input are left to default.
185 |
186 | get_user_input prompt="Enter the year:" TYPE=int BOUNDS_LO=1900 BOUNDS_HI=2500 RCVR_MACRO=_ui_test_get_mo_name
187 |
188 | _proc_end function=_ui_test_get_year
189 |
190 | #--------------------------------------------------------------------
191 |
192 | [gcode_macro _ui_test_get_mo_name]
193 | description: second test proc - chained here by parameters from the first test proc
194 | gcode:
195 |
196 | _proc_start function=_ui_test_get_mo_name func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
197 |
198 | SAVE_VARIABLE VARIABLE=ui_test_year VALUE='{params.UI_INPUT}' # save the value for later use
199 |
200 | M118 Outstanding. You entered Year: {params.UI_INPUT}
201 | M118 Next we're going to be asking for the month name in the form of a string. For strings, the bounding
202 | M118 parameters are used to enforce entered string length limits. In the case of month name, bounding limits
203 | M118 will be set to accept no less than 3 characters (May is the shortest named month) and no more than
204 | M118 9 characters (September is the longest named month). For TYPE enforcement validation, try to enter an
205 | M118 Integer or Float, or try to enter a string with fewer than 3 or more than 9 characters - 60sec timeout
206 |
207 | get_user_input prompt="Enter the name of the month:" TYPE=str BOUNDS_LO=3 BOUNDS_HI=9 RCVR_MACRO=_ui_test_get_mo_num TO_PERIOD=60
208 |
209 | _proc_end function=_ui_test_get_mo_name
210 |
211 | #--------------------------------------------------------------------
212 |
213 | [gcode_macro _ui_test_get_mo_num]
214 | description:
215 | gcode:
216 |
217 | _proc_start function=_ui_test_get_mo_num func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
218 |
219 | SAVE_VARIABLE VARIABLE=ui_test_mo_name VALUE='{params.UI_INPUT}' # save the value for later use
220 |
221 | M118 Great. You entered month: {params.UI_INPUT}
222 | M118 Next we're going to be asking for the month number in the form of a bound integer (1-12).
223 | M118 You will have 45 seconds to do this. Be aware that too short of a TO_PERIOD will net drama for
224 | M118 the user. The default TO_PERIOD is 120 seconds - recommended caution with short TO_PERIODs, and
225 | M118 to not have the exception handler be destructive when a timeout occurs - life happens.
226 |
227 | get_user_input prompt="Enter the number of the month:" TYPE=int BOUNDS_LO=1 BOUNDS_HI=12 RCVR_MACRO=_ui_test_get_day_num TO_PERIOD=45
228 |
229 | _proc_end function=_ui_test_get_mo_num
230 |
231 | #--------------------------------------------------------------------
232 |
233 | [gcode_macro _ui_test_get_day_num]
234 | description:
235 | gcode:
236 |
237 | _proc_start function=_ui_test_get_day_num func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
238 |
239 | SAVE_VARIABLE VARIABLE=ui_test_mo_num VALUE='{params.UI_INPUT}' # save the value for later use
240 |
241 | M118 Noice. You entered month number: {params.UI_INPUT}
242 | M118 Next we're going to be asking for the day in the month in the form of a bound integer (1-31).
243 | M118 You will have 45 seconds to input this value.
244 |
245 | get_user_input prompt="Enter the number of the day of the month:" TYPE=int BOUNDS_LO=1 BOUNDS_HI=31 RCVR_MACRO=_ui_test_get_day_name TO_PERIOD=45
246 |
247 | _proc_end function=_ui_test_get_day_num
248 |
249 | #--------------------------------------------------------------------
250 |
251 | [gcode_macro _ui_test_get_day_name]
252 | description:
253 | gcode:
254 |
255 | _proc_start function=_ui_test_get_day_name func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
256 |
257 | SAVE_VARIABLE VARIABLE=ui_test_day_num VALUE='{params.UI_INPUT}' # save the value for later use
258 |
259 | M118 Good. You entered day number: {params.UI_INPUT}
260 | M118 Next we're going to be asking for the day name in the form of a string. Bounding limits set to
261 | M118 6 to 9 string characters.
262 |
263 | get_user_input prompt="Enter the name of the day today:" TYPE=str BOUNDS_LO=6 BOUNDS_HI=9 RCVR_MACRO=_ui_test_get_float
264 |
265 | _proc_end function=_ui_test_get_day_name
266 |
267 | #--------------------------------------------------------------------
268 |
269 | [gcode_macro _ui_test_get_float]
270 | description:
271 | gcode:
272 |
273 | _proc_start function=_ui_test_get_float func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
274 |
275 | SAVE_VARIABLE VARIABLE=ui_test_day_name VALUE='{params.UI_INPUT}' # save the value for later use
276 |
277 | M118 Wicked kewl. You entered day name of: {params.UI_INPUT}
278 | M118 Next we're going to be asking for a float (decimal number). These are default bound to values ranging
279 | M118 from -999999999.0 to 999999999.0.
280 |
281 | get_user_input prompt="Enter a float:" TYPE=flt RCVR_MACRO=_ui_test_conclude
282 |
283 | _proc_end function=_ui_test_get_float
284 |
285 | #--------------------------------------------------------------------
286 |
287 | [gcode_macro _ui_test_conclude]
288 | description:
289 | gcode:
290 |
291 | _proc_start function=_ui_test_conclude func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
292 |
293 | SAVE_VARIABLE VARIABLE=ui_test_float VALUE='{params.UI_INPUT}' # save the value for later use
294 |
295 | M118 Good deal. You entered the value: {params.UI_INPUT}
296 | M118 The author of this module may expand this test/demo demonstrating use/validation of button
297 | M118 presses, but hasn't yet identified a time frame for doing so. Stand by for same... :)
298 |
299 | get_user_input prompt="Press any of the UI buttons below to conclude this test/demo:" TYPE=str RCVR_MACRO=_ui_test_concluded
300 |
301 | _proc_end function=_ui_test_conclude
302 |
303 | #--------------------------------------------------------------------
304 |
305 | [gcode_macro _ui_test_concluded]
306 | description:
307 | gcode:
308 |
309 | _proc_start function=_ui_test_concluded func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
310 |
311 | M118 AAAnnndddd, you clicked/entered: {params.UI_INPUT}
312 |
313 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
314 |
315 | {% set ui_test_interaction_results =
316 | "Year: " ~ svv.ui_test_year ~ "
" ~
317 | "Month, Num: " ~ svv.ui_test_mo_name ~ ", " ~ svv.ui_test_mo_num ~ "
" ~
318 | "Day, Num: " ~ svv.ui_test_day_name ~ ", " ~ svv.ui_test_day_num ~ "
" ~
319 | "Entered Float: " ~ svv.ui_test_float ~ "
"
320 | %}
321 |
322 | M118 {"Codified and Saved Test Results:
" ~ ui_test_interaction_results}
323 | M118 This concludes the demo of the UI system. This was only a test. Had this been a real use case
324 | M118 things would have certianly been less verbose and more efficient, and your steppers might not have melted. :)
325 | M118
326 | M118 Have a great day. Happy Printing!!! -MHz
327 |
328 | _proc_end function=_ui_test_concluded
329 |
330 | #--------------------------------------------------------------------
331 |
332 | [gcode_macro STOP_DEMO] # for UI button
333 | description:
334 | gcode:
335 |
336 | _proc_start function=STOP_DEMO func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
337 |
338 | _stop_ui_input_loop
339 | _stop_ui_reminder
340 | _stop_ui_timeout_watchdog
341 | _ui_clear_cache
342 | _ui_clear_test_cache
343 |
344 | _proc_end function=STOP_DEMO
345 |
346 | #--------------------------------------------------------------------
347 |
348 | [gcode_macro START_DEMO] # for UI button
349 | description:
350 | gcode:
351 |
352 | _proc_start function=START_DEMO func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
353 |
354 | _start_ui_test
355 |
356 | _proc_end function=START_DEMO
357 |
358 | #--------------------------------------------------------------------
359 |
--------------------------------------------------------------------------------
/_user_interaction.cfg:
--------------------------------------------------------------------------------
1 | #--------------------------------------------------------------------
2 | # _user_interaction (UI) gcode 'module'
3 | # Copyright (C) 2022-2023 Tod A. Wulff
4 | #
5 | # This program is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # Should you have a need to receive a copy of the GNU General Public License,
16 | # see .
17 | #--------------------------------------------------------------------
18 | # https://github.com/TodWulff/Klipper_UserInteraction
19 | #--------------------------------------------------------------------
20 | # Module Includes
21 | [include _ui_test.cfg] # includes testing macros and a set of interrelated UI demonstration macros
22 |
23 | #--------------------------------------------------------------------
24 |
25 | [delayed_gcode _user_interaction_loaded] # template macro for user module loading advisement, when so enabled
26 | initial_duration: 4.501
27 | gcode:
28 | _proc_start function=_user_interaction_loaded func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
29 |
30 | _module_loaded MODULE=_user_interaction
31 |
32 | _proc_end function=_user_interaction_loaded
33 |
34 | #--------------------------------------------------------------------
35 |
36 | [gcode_macro _info_ui_module] # template macro with module prerequisites/documentation/notes/examples/etc.
37 |
38 | gcode:
39 | # This macro _info_ui_module is for module documentation only - isn't intended to be called - no gcode herein is intended to be ran
40 |
41 | # This klipper gcode macro module heavily relies on [Save_Variables] module
42 | # see: https://github.com/TodWulff/V2.2526_Config/blob/main/_persistent_variables.cfg
43 |
44 | # This gcode module makes use of M300 and custom M300 related macros for emitting sounds, so if you're not so
45 | # interested, or haven't a beeper on your box, comment out related lines: _ui_reminder, _annunciate_input_exception,
46 | # and _annunciate_good_input - see: https://github.com/TodWulff/V2.2526_Config/blob/main/_m300_sounds.cfg
47 |
48 | # This gcode module makes heavy use of the [response] module - stock M118 trapped (using rename_existing) so
49 | # that it uses action_notification vs. FW M118 code. This enables emission of 'special characters' that M118's
50 | # FW code chokes on - see: https://github.com/TodWulff/V2.2526_Config/blob/main/_gcode_macros.cfg
51 |
52 | # Throughout my configs, virtually all gcode has been 'instrumented' - the first/last set if lines in each gcode
53 | # block can/should be deleted. I have these as I have an ability to trace macro code execution and display same
54 | # via console - powerful for macro development/troubleshooting but likely of little/no use to others
55 | # see: https://github.com/TodWulff/V2.2526_Config/blob/main/_debug_macros.cfg
56 |
57 | # Users CAN/should add a User Input macro pane, depicted: https://i.imgur.com/QVxLuVZ.png, which calls macros herein
58 | # Orienting it under the console pane will allow it to become intuitive with a small bit of use
59 |
60 | # Primary Macro: GET_USER_INPUT Parameters and related dialog follows
61 |
62 | # Optional Parameters:
63 | # PROMPT Text to display in console as a user input prompt
64 | # RCVR_MACRO Macro name to run when VALID input received - UI_INPUT param passed to proc - if rcv'd, query svv for moar info
65 | # TYPE The TYPE of input needed - one of these three string/str, integer/int, float/flt
66 | # BOUNDS_HI if TYPE is float/flt, input must be >=lo and <=hi - for integer/int, input must be >=floor(lo) and <=ceiling(hi)
67 | # BOUNDS_LO - if string/str TYPE, character count must be >=floor(lo) and <=ceiling(hi)
68 | # TO_PERIOD Period in Integer seconds to wait for user input - a reminder M300 fires at rate in _ui_vars while waiting
69 | # EXCPT_HDLR Macro name is called in the event of an input timeout or faulty input - no params passed - query svv...
70 | # TO_CYCL_DEF Defaults to -1 (disabled) >=0 integer TO_period expiration cycles to occur before TO_DEF_RESP is sent to RCVR_MACRO
71 | # TO_RESP_DEF if TO_CYCLES >=0, this param will be passed to RCVR_MACRO via UI_INPUT if no user input received
72 | # RMDR_PERIOD overrides reminder period in _ui_vars
73 |
74 | # For string TYPE, if BOUNDS_LO is not asserted, defaults to 1
75 | # For string TYPE, if BOUNDS_HI is not asserted, defaults to 255
76 |
77 | # For float TYPE, if BOUNDS_LO is not asserted, defaults to -999999999.0
78 | # For float TYPE, if BOUNDS_HI is not asserted, defaults to 999999999.0
79 |
80 | # For integer TYPE, if BOUNDS_LO is not asserted, defaults to -999999999
81 | # For integer TYPE, if BOUNDS_HI is not asserted, defaults to 999999999
82 |
83 | # If additional bounds testing is desired/required, it's up to the user implementing this on their printers to craft more granular
84 | # test and validation - by way of the RCVR_MACRO. If I've blatantly missed something, then let me know. :)
85 |
86 | # For optional parameters, read the code to understand implications of relying on defaults.
87 |
88 | # Again, ALL options are optional and default to something - defaults are as entered or noted
89 | # get_user_input PROMPT="Enter or Click something:" # displayed on the console at macro start as a user prompt
90 | # TYPE=STRING # 'string' or 'integer' or 'float' - for buttons use string
91 | # BOUNDS_LO=1 (Int/Flt -999999999) # min string chars or min numerical value (int/flt)
92 | # BOUNDS_HI=255 (Int/Flt 999999999) # max string chars or max numerical value (int/flt)
93 | # RCVR_MACRO=_test_show_user_input # to accept param UI_INPUT that will be an int/flt/"string" that was
94 | # # input and passes sniff (simple bounds) test
95 | # TO_PERIOD=120 # in seconds
96 | # EXCPT_HDLR=_ui_exception_handler # no params passed - use svv to get runtime specifics if needed
97 | # TO_CYCL_DEF - Defaults to -1 (disabled) >=0 integer TO_period expiration cycles to occur before TO_DEF_RESP is sent to RCVR_MACRO
98 | # TO_RESP_DEF - if TO_CYCLES >=0, this param will be passed to RCVR_MACRO via UI_INPUT if no user input received
99 | # RMDR_PERIOD # overrides reminder period in _ui_vars
100 | #
101 | # Example call follows:
102 | # This looks for a user to enter a string 1-12 chars long, with a timeout of 30 secs, that forwards (via UI_INPUT param),
103 | # the entered string to the '_test_show_user_input' macro (default if no RCVR_MACRO passed by user call)
104 | # If a timeout happens/faulty input is detected, the _ui_timeout_watchdog/_validate_user_input macros call '_ui_exception_handler' macro
105 | # (which is the default exception handler if no EXCPT_HDLR macro name is passed by the user call)
106 | # being reminded every 10 seconds and after 1 requery and subsequent timeouts, "YES" will be sent as a default ui input response.
107 | get_user_input PROMPT="Enter or Click something:" TYPE=string BOUNDS_LO=1 BOUNDS_HI=12 RCVR_MACRO=_test_show_user_input TO_PERIOD=30 RMDR_PERIOD=10 EXCPT_HDLR=_ui_exception_handler TO_CYCL_DEF=1 TO_RESP_DEF="YES"
108 |
109 | # EXCEPTION HANDLER MACRO:
110 | # if a custom EXCPT_HDLR macro is to be instantiated, it may prove useful to consider the following:
111 | # - GET_USER_INPUT does the following:
112 | # a. initializes states and then displays the user PROMPT
113 | # b. sets the timeout watchdog to fire after the passed/default TO_PERIOD
114 | # c. puts _await_user_input into a recursive loop
115 | # - a single EXCPT_HDLR macro services both bad input cases as well as the time-out when waiting on user input.
116 | # - for timeouts the default EXCPT_HDLR macro simply recalls GET_USER_INPUT giving user another input context
117 | # - this approach can be altered with a custom EXCPT_HDLR macro being passed to the GET_USER_INPUT call
118 | # - When _await_user_input senses user input, _validate_user_input tests for TYPE and BOUNDS_HI/BOUNDS_LO compliance
119 | # - if input is NOT TYPE & BOUNDS_HI/BOUNDS_LO compliant, _await_user_input recalls GET_USER_INPUT so user can fix it
120 | # - if input IS TYPE & BOUNDS_HI/BOUNDS_LO compliant (_ui_bad_input NOT set), input is sent to RCVR_MACRO via UI_INPUT param
121 |
122 | # If a custom EXCPT_HDLR macro is employed, when/if the get_user_input {svv.ui_input_macro_rawparams} is called to requery,
123 | # be sure to include the helper param REQUERY=1, else the countdown to default response won't work - i.e.:
124 | # get_user_input {svv.ui_input_macro_rawparams} REQUERY=1
125 | # other use of this param is done at your own risk... :)
126 |
127 | # in most conceivable use cases, the default exception handler/validation macros should be adequate, but want to give others
128 | # options, in the event I haven't considered something. Either an entirely new EXCPT_HDLR can be crafted or, as demonstrated
129 | # in _ui_test.cfg's _ui_test_exception_handler (custom EXCPT_HDLR) code runs and then calls the stock _ui_exception_handler below
130 |
131 | # When sending UI_INPUT to RCVR_MACRO and a string is the asserted response TYPE, when performing conditional
132 | # tests in the RCVR_MACRO, they should have the form of (note the '" pairs that surround the contents):
133 | # https://i.imgur.com/2FOqUzQ.png
134 |
135 | # In _ui_vars, a person implementing this can selectively enable/disable Input Prompt and/or Exception hints. Hints are
136 | # little descriptive blurbs as to what the code is expecting as input - one can enable hints on either the input prompt,
137 | # or disable hints when an input exception is detected/raised. It is suggested that it is likely best to have exception
138 | # hints enabled, and to have the input prompt detail what sort of input is desired. The _ui_test.cfg file has the START_DEMO
139 | # macro that iterates through some UI input event - I used it for dev testing, it works as a demo.
140 | # Some visual examples as it relates to the hint options that can be set in _ui_vars:
141 |
142 | # No Hints on Exception nor Input Prompt: https://i.imgur.com/PDPOmTJ.png (likely too? terse - make prompt/preamble detailed)
143 | # Hints on Exception only, not on Input Prompt: https://i.imgur.com/D5Ih6hE.png (the author's preference)
144 | # Hints on Input Prompt but not on an Exception: https://i.imgur.com/Deo2SNr.png (leads to some noise, which may be tolerable)
145 | # Hints on both Input Prompt and when an exception is raised: https://i.imgur.com/mO7TfWW.png (likely too chatty & redundant, imo)
146 |
147 | #--------------------------------------------------------------------
148 |
149 | [gcode_macro _ui_vars] # template macro for user module - base config default variables for the _User_Interaction module
150 | description: ui module variables
151 |
152 | # Defaults to be used if param not passed when GET_USER_INPUT called for:
153 | # PROMPT, TYPE, BOUNDS_LO, BOUNDS_HI, RCVR_MACRO, TO_PERIOD, EXCPT_HDLR, TO_CYCL_DEF, TO_RESP_DEF, RMDR_PERIOD
154 |
155 | variable_ui_input_def_param_prompt: "Awaiting User Input:" # Text to display in console as a user input prompt
156 | variable_ui_input_def_param_type: "STRING" # The TYPE of input needed - one of these three string/str, integer/int, float/flt
157 | variable_ui_input_def_param_bounds_lo_str: 1 # - if string/str TYPE, character count must be >=floor(lo) and <=ceiling(hi)
158 | variable_ui_input_def_param_bounds_hi_str: 255 # if TYPE is float/flt, input must be >=lo and <=hi - for integer/int, input must be >=floor(lo) and <=ceiling(hi)
159 | variable_ui_input_def_param_bounds_lo_num: -999999999 # integer or float input default max value accepted
160 | variable_ui_input_def_param_bounds_hi_num: 999999999 # integer or float input default min value accepted
161 | variable_ui_input_def_param_rcvr_macro: "_test_show_user_input" # Macro name to run when VALID input received - UI_INPUT param passed to proc - if reqd, query svv for moar info
162 | variable_ui_input_def_param_to_period: 120 # Period in Integer seconds to wait for user input - a reminder M300 fires at rate in _ui_vars while waiting
163 | variable_ui_input_def_param_excpt_hdlr: "_ui_exception_handler" # Macro name is called in the event of an input timeout or faulty input - no params passed - query svv...
164 | variable_ui_input_def_param_to_cycl_def: -1 # Defaults to -1 (disabled) >=0 integer TO_period expiration cycles to occur before TO_DEF_RESP is sent to RCVR_MACRO
165 | variable_ui_input_def_param_to_resp_def: "NULL" # if TO_CYCLES >=0, this param will be passed to RCVR_MACRO via UI_INPUT if no user input received
166 | variable_ui_input_def_param_rmdr_period: 15 # reminder bleeps every n seconds - 0 will disable reminder beeps
167 |
168 | # input scan loop iteration period (salient only when awaiting user input)
169 | variable_ui_input_check_recurse_period: 0.5 #seconds - period between checking for input when expected - 0.5s is good.
170 | # more delay (slower testing iteration) and UX presents as less responsive
171 | # less delay and host/klipper may get needlessly tasked (admitted speculation)
172 | # aural reminder related vars
173 | variable_ui_reminder_enable: 1 #bool 1/0 - 0 overtly disables reminder bleeps regardless of RMDR_PERIOD param
174 |
175 | # expected input hinting on user input promps and exceptions
176 | variable_ui_enable_input_hints: 0 #bool 1/0 - 1 enabled module-generated input hints - 0 disables same in favor of 'macro hinting/prompts'
177 | variable_ui_disable_exception_hints: 0 #bool 1/0 - 1 disables exception hint emissions - prolly want this == 0...
178 | variable_ui_input_readback_flag: 0 #bool 1/0 - 1 enabled readback of user input value - overly chatty, but helpful for peeps with accessibility issues, maybe
179 | variable_ui_button_readback_flag: 0 #bool 1/0 - 1 enabled readback of user UI button click - overly chatty, but helpful for peeps with accessibility issues, maybe
180 |
181 | # in this case, used to squelch chatty input status polling in trace logs
182 | variable_debug_trace_emissions: 0 # 0=quiet 1=chatty (controls logging of _await_user_input proc)
183 |
184 |
185 | #ToDo - not yet implemented:
186 | #variable_ui_force_pause_on_query: 1 #bool 1/0
187 |
188 | # did you hear my eyes cage when they pitched through zenith on their way to nadir???
189 | #variable_ui_he_timeout_on_input_timeout: 0 #bool 1/0
190 | #variable_ui_hotend_timeout_period: 0 #period in secs
191 | #variable_ui_bed_timeout_on_input_timeout: 0 #bool 1/0
192 | #variable_ui_bed_timeout_period: 0 #period in secs
193 |
194 | # this is a goal implementation, as it will ease module use
195 | #variable_ui_unified_input_field: 0 #bool 1/0
196 |
197 | #dunno if this is of value or not. will use for a bit before deciding to embrace or forego this
198 | #variable_ui_bypass_type_enforcement: 0 #bool 1/0
199 | #variable_ui_bypass_bounds_enforcement: 0 #bool 1/0
200 |
201 | gcode:
202 |
203 | _proc_start function=_ui_vars func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
204 |
205 | # there is none - for 'gcode module' variable use only
206 |
207 | _proc_end function=_ui_vars
208 |
209 | #--------------------------------------------------------------------
210 |
211 | [delayed_gcode _ui_module_start] # template macro for user module - for module initialization purposes
212 | #description: Sets module-specific state flags for conditional use elsewhere.
213 | initial_duration: 0.5 # have this at 0.5s as init code in _startup_autoexec.cfg runs at 0.1s after start
214 | gcode:
215 |
216 | _proc_start function=_ui_module_start func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
217 |
218 | _ui_clear_cache
219 |
220 | SAVE_VARIABLE VARIABLE=ui_input_to_cycl_def VALUE=-1 #internal decremented counter to force a default response in event of n timeouts.
221 | SAVE_VARIABLE VARIABLE=module_ui_loaded VALUE=1 #flag via persistent variable that this module is loaded
222 | SAVE_VARIABLE VARIABLE=ui_err_flag VALUE=0 #init error flag via persistent variable that this module is not in error
223 |
224 | _proc_end function=_ui_module_start
225 |
226 | #--------------------------------------------------------------------
227 |
228 | [delayed_gcode _ui_reminder] # provides repeating aural and verbal indications that user input is being sought
229 | # provides a repeating alarm to alert on a requirement for user input
230 | initial_duration: 0
231 | gcode:
232 |
233 | _proc_start function=_ui_reminder func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
234 |
235 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
236 |
237 | #do things here like flash leds, make noise, etc. for user attention purposes
238 | M300 P100 S3000
239 | say_wait S="Awaiting Input"
240 | # get cuter here and read back the prompt - need to scrub parenthized stuff, including parens...
241 | # get cute here with monitoring the count down to default response and progressively annoy the user...
242 |
243 | UPDATE_DELAYED_GCODE ID=_ui_reminder DURATION={svv.ui_input_rmdr_period}
244 |
245 | _proc_end function=_ui_reminder
246 |
247 | #--------------------------------------------------------------------
248 |
249 | [gcode_macro _start_ui_reminder] # starts the repetative loop of reminding the user that input is being sought
250 | gcode:
251 |
252 | _proc_start function=_start_ui_reminder func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
253 |
254 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
255 |
256 | UPDATE_DELAYED_GCODE ID=_ui_reminder DURATION={svv.ui_input_rmdr_period}
257 |
258 | _proc_end function=_start_ui_reminder
259 |
260 | #--------------------------------------------------------------------
261 |
262 | [gcode_macro _stop_ui_reminder] # stops the repetative reminder at receipt of some input
263 | gcode:
264 |
265 | _proc_start function=_stop_ui_reminder func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
266 |
267 | UPDATE_DELAYED_GCODE ID=_ui_reminder DURATION=0 # stop the recursive pause alarm
268 |
269 | _proc_end function=_stop_ui_reminder
270 |
271 | #--------------------------------------------------------------------
272 |
273 | [delayed_gcode _ui_timeout_watchdog] # monitors overall input response time and fires when times out to respond with default - != reminder
274 | #description
275 | initial_duration: 0
276 | gcode:
277 | _proc_start function=_ui_timeout_watchdog func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
278 |
279 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
280 |
281 | # we've timed out, so basically kill ui input stuff and call the exception handler proc w/o parameters
282 | # one can sniff svv for called parameters if needed
283 | _stop_ui_input_loop
284 | _stop_ui_timeout_watchdog
285 | _stop_ui_reminder
286 |
287 | # svv.ui_input_to_cycl_def defaults to a -1 if not passed
288 | {% if svv.ui_input_to_cycl_def|int >= 1 %}
289 | SAVE_VARIABLE VARIABLE=ui_input_to_cycl_def VALUE={svv.ui_input_to_cycl_def|int - 1}
290 | {% endif %}
291 |
292 | # send ui_input_to_resp_def to ui_input_rcvr_macro if timeout count reaches exactly 0
293 | {% if svv.ui_input_to_cycl_def|int == 0 %}
294 |
295 |
296 | #woops, I think I forgot to edit this to reflect a default message vs. input hinting - lol - too many hours at it, I guess.
297 |
298 | # #annunciating that default response is being used due to timeout thresholds/cycle counts tripping
299 | # {% set emission = "-------------------------------
" ~
300 | # "" ~ hint_type ~ " input needed (w/in " ~ ui_input_to_period ~ "s): " ~ ui_hint ~ "
" ~
301 | # "" ~ ui_input_prompt ~ "
" ~
302 | # "-------------------------------
" %}
303 | #
304 | # M118 {emission}
305 | #
306 | # send default response
307 | _annunciate_good_input
308 | # should I better mock UI by calling processing (bypass validation as default is programmatically asserted.?.)
309 | #fixme?
310 | #{svv.ui_input_rcvr_macro} UI_INPUT='"{svv.ui_input_to_resp_def|replace("\"","\\\"")}"'
311 | _process_user_input UI_INPUT='"{svv.ui_input_to_resp_def|replace("\"","\\\"")}"'
312 | {% else %}
313 | # call the to exception handler proc - codified below to default to the _ui_exception_handler macro
314 | {svv.ui_input_excpt_hdlr}
315 | {% endif %}
316 |
317 | _proc_end function=_ui_timeout_watchdog
318 |
319 | #--------------------------------------------------------------------
320 |
321 | [gcode_macro _start_ui_timeout_watchdog] # starts the watchdog - trigger at begining of input cycle
322 | description: Start the User Input Timeout Watchdog - param DURATION sets period
323 | gcode:
324 |
325 | _proc_start function=_start_ui_timeout_watchdog func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
326 |
327 | UPDATE_DELAYED_GCODE ID=_ui_timeout_watchdog DURATION={params.DURATION|int} # start a recursive pause alarm
328 |
329 | _proc_end function=_start_ui_timeout_watchdog
330 |
331 | #--------------------------------------------------------------------
332 |
333 | [gcode_macro _stop_ui_timeout_watchdog] # stops the timeout watchdog - triggered at a timeout and also when input is received
334 | gcode:
335 |
336 | _proc_start function=_stop_ui_timeout_watchdog func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
337 |
338 | UPDATE_DELAYED_GCODE ID=_ui_timeout_watchdog DURATION=0 # stop the recursive pause alarm
339 |
340 | _proc_end function=_stop_ui_timeout_watchdog
341 |
342 | #--------------------------------------------------------------------
343 |
344 | [delayed_gcode _await_user_input] # this is the working delayed gcode that tests for input being received (leans on svv a lot...)
345 | #description
346 | # RCVR_MACRO = macro to run upon validated input
347 | # can't pass dynamic parameters to delayed_gcode? so need to load RCVR_MACRO from saved variables
348 | # had to make use of namespace workaround for in-proc flag tracking
349 | initial_duration: 0
350 | gcode:
351 |
352 | # conditionally muting trace emissions for now, given how chatty this proc is (running every 0.5S...)
353 | {% if printer["gcode_macro _ui_vars"].debug_trace_emissions %}
354 | _proc_start function=_await_user_input func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
355 | {% endif %}
356 |
357 | {% set this = namespace(no_ui_input=1) %} # work around for change visibility in same proc instance
358 |
359 | # this is for the case when a user enters a string that contains spaces or w/e
360 | {% set this2 = namespace(ui_string_flag=0) %} # work around for change visibility in same proc instance
361 |
362 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
363 |
364 | {% set UI_Input = "Um, no UI_INPUT passed" %}
365 |
366 | # These here are checks to see if input received
367 | # TODO: consider using afi code to dynamically process the user input and simplify the code...
368 |
369 | # FYSA: Logically, Buttons return a STRING value, in addition to having a CLICK state flag tripped
370 | # this conditional set serves to limit focus to the TYPE expected - that way a stale integer/float entry
371 | # doesn't get associated as a STRING value and vice versa
372 | {% if svv.ui_input_type|upper == "STRING" %}
373 |
374 | {% if svv.ui_click_lt %}
375 | {% set this.no_ui_input = 0 %} #clear flag
376 | {% set UI_Input = "LT" %}
377 | {% endif %}
378 |
379 | {% if svv.ui_click_rt %}
380 | {% set this.no_ui_input = 0 %} #clear flag
381 | {% set UI_Input = "RT" %}
382 | {% endif %}
383 |
384 | {% if svv.ui_click_up %}
385 | {% set this.no_ui_input = 0 %} #clear flag
386 | {% set UI_Input = "UP" %}
387 | {% endif %}
388 |
389 | {% if svv.ui_click_dn %}
390 | {% set this.no_ui_input = 0 %} #clear flag
391 | {% set UI_Input = "DN" %}
392 | {% endif %}
393 |
394 | {% if svv.ui_click_yes %}
395 | {% set this.no_ui_input = 0 %} #clear flag
396 | {% set UI_Input = "YES" %}
397 | {% endif %}
398 |
399 | {% if svv.ui_click_no %}
400 | {% set this.no_ui_input = 0 %} #clear flag
401 | {% set UI_Input = "NO" %}
402 | {% endif %}
403 |
404 | {% if svv.ui_click_conf %}
405 | {% set this.no_ui_input = 0 %} #clear flag
406 | {% set UI_Input = "CONFIRM" %}
407 | {% endif %}
408 |
409 | {% if svv.ui_click_canx %}
410 | {% set this.no_ui_input = 0 %} #clear flag
411 | {% set UI_Input = "CANCEL" %}
412 | {% endif %}
413 |
414 | {% if svv.ui_user_input_string != "" %}
415 | {% set this.no_ui_input = 0 %} #clear flag
416 | {% set UI_Input = svv.ui_user_input_string %}
417 | {% set this2.ui_string_flag = 1 %} #set flag to pass '"string"' vs. string (raw)
418 | {% endif %}
419 |
420 | {% elif svv.ui_input_type|upper == "INTEGER" %}
421 |
422 | {% if svv.ui_user_input_integer != -9999999 %}
423 | {% set this.no_ui_input = 0 %} #clear flag
424 | {% set UI_Input = svv.ui_user_input_integer %}
425 | {% endif %}
426 |
427 | {% elif svv.ui_input_type|upper == "FLOAT" %}
428 |
429 | {% if svv.ui_user_input_float != -9999999.99 %}
430 | {% set this.no_ui_input = 0 %} #clear flag
431 | {% set UI_Input = svv.ui_user_input_float %}
432 | {% endif %}
433 |
434 | {% endif %}
435 |
436 | {% if this.no_ui_input == 1 %}
437 |
438 | # No User Input so continue to recurse until to is reached or user input is provided
439 | UPDATE_DELAYED_GCODE ID=_await_user_input DURATION={printer["gcode_macro _ui_vars"].ui_input_check_recurse_period} SHH=1 # mute chatty logging
440 |
441 | {% else %}
442 |
443 | # ok, we got some input so kill timeout & reminder beeps
444 | _stop_ui_timeout_watchdog
445 | _stop_ui_reminder
446 |
447 | # fixme? does this need to kill itself, since we did get some input - need to peel back the gray matter fog...
448 |
449 | # validate user input for TYPE and BOUNDS_LO/BOUNDS_HI conformance
450 | _validate_user_input # if input is deemed 'faulty' (type or bounds escape), this macro flags via svv.ui_bad_input
451 |
452 | #enable_code_trace
453 | _process_user_input UI_INPUT='{UI_Input}' IS_STRING={this2.ui_string_flag}
454 | #disable_code_trace
455 |
456 | {% endif %}
457 |
458 | # conditionally muting trace emissions for now, given how chatty this proc is (running every 0.5S...)
459 | {% if printer["gcode_macro _ui_vars"].debug_trace_emissions %}
460 | _proc_end function=_await_user_input
461 | {% endif %}
462 |
463 | #--------------------------------------------------------------------
464 |
465 | [gcode_macro _start_ui_input_loop] # this starts a recursive loop that fires every blah period to look for and flag input as being received
466 | gcode:
467 |
468 | _proc_start function=_start_ui_input_loop func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
469 |
470 | #bleep to intimate that it is waiting for input
471 | M300 P100 S3000
472 | # say_wait S="Awaiting Input"
473 | UPDATE_DELAYED_GCODE ID=_await_user_input DURATION={printer["gcode_macro _ui_vars"].ui_input_check_recurse_period} # start a recursive pause alarm
474 |
475 | _proc_end function=_start_ui_input_loop
476 |
477 | #--------------------------------------------------------------------
478 |
479 | [gcode_macro _stop_ui_input_loop] # this stops the recursive every blah period loop that looks for and flags an input as being received
480 | gcode:
481 |
482 | _proc_start function=_stop_ui_input_loop func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
483 |
484 | UPDATE_DELAYED_GCODE ID=_await_user_input DURATION=0 # stop the recursive pause alarm
485 |
486 | _proc_end function=_stop_ui_input_loop
487 |
488 | #--------------------------------------------------------------------
489 |
490 | [delayed_gcode GET_USER_INPUT_DELAYED] # delayed gcode to implement the GET_USER_INPUT_DELAY macro
491 | #description: allows for delayed utilization of GET_USER_INPUT
492 | initial_duration: 0
493 | gcode:
494 | _proc_start function=GET_USER_INPUT_DELAYED func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
495 |
496 | #need to fetch params from svv here
497 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
498 |
499 | # may need to strip string encapsulation.?.
500 | GET_USER_INPUT {svv.ui_input_delayed_ui_params}
501 |
502 | _proc_end function=GET_USER_INPUT_DELAYED
503 |
504 | #--------------------------------------------------------------------
505 |
506 | [gcode_macro GET_USER_INPUT_DELAY] # <----<<< MAIN MACRO w/ non-blocking Delayed Execution (param.DELAY=blah [float seconds])
507 | description: Enables delayed utilization of GET_USER_INPUT
508 | # save rawarams to svv.ui_input_delayed_ui_params and setup delayed_gcode to fire GET_USER_INPUT_DELAYED in param.DELAY seconds
509 | gcode:
510 |
511 | _proc_start function=GET_USER_INPUT_DELAY func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
512 |
513 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
514 |
515 | {% set ui_input_delayed_ui_params = rawparams|replace("\"","\\\"") %}
516 |
517 | save_variable VARIABLE=ui_input_delayed_ui_params VALUE='"{ui_input_delayed_ui_params|string}"'
518 |
519 | UPDATE_DELAYED_GCODE ID=GET_USER_INPUT_DELAYED DURATION={params.DELAY|float} # ping user for input in DELAY seconds
520 |
521 | _proc_end function=GET_USER_INPUT_DELAY
522 |
523 | #--------------------------------------------------------------------
524 |
525 | [gcode_macro GET_USER_INPUT] # <----<<< MAIN MACRO for parsing a GUI request, setting up stuffs, & emitting query to console
526 | description: main macro for waiting for user input - if passed, TYPE will determine required input - defaults to string of 1-255 characters
527 | # PROMPT to display in console before user input
528 | # TYPE - the TYPE of input needed - string/str, integer/int, float/flt
529 | # BOUNDS_HI - for Integer/Float TYPE, user input must be >=low and <=high
530 | # BOUNDS_LO - if string TYPE, ceiling(low)/ceiling(hi) drive char count checks
531 | # RCVR_MACRO proc to run after input received - said proc will need to query svv for values
532 | # TO_PERIOD - period in seconds to wait for user input
533 | # EXCPT_HDLR - proc to call in the event of a user input timeout
534 |
535 | # TO_CYCL_DEF - Defaults to -1 (disabled) >0 integer TO_period expiration cycles to occur before TO_DEF_RESP is sent to RCVR_MACRO
536 | # TO_RESP_DEF - if TO_CYCLES >0 this param will be passed to RCVR_MACRO via UI_INPUT if no user input received after TO_CYCL_DEF timeouts
537 | # RMDR_PERIOD - period between reminder bleeps (0 disabled)
538 |
539 | # internal parameter is used for timeout requeries - REQUERY=1 if so
540 |
541 | #TODO:s
542 | # react based on state - if idle, if printing, if paused, if error, if ...
543 | # may need to impute a pause if not already done and in a print.?.
544 |
545 | gcode:
546 |
547 | # enable_code_trace
548 |
549 | _proc_start function=get_user_input func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
550 |
551 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
552 |
553 | _ui_clear_cache
554 |
555 | {% if params.REQUERY|default(0)|int == 0 %}
556 | # clear this decremented counter var ONLY on initial query, not automated requeries
557 | # this is a countdown variable to enable automatic default responses after
558 | # n timeouts, so its value needs to survive across requeries
559 |
560 | # this is the iterations to timeout before TO_RESP_DEF is sent to RCVR_MACRO
561 | # logic elsewhere prevents TO_RESP_DEF being sent to RCVR_MACRO if TO_CYCL_DEF <0
562 | {% set ui_input_to_cycl_def = params.TO_CYCL_DEF|default(printer["gcode_macro _ui_vars"].ui_input_def_param_to_cycl_def) %}
563 | SAVE_VARIABLE VARIABLE=ui_input_to_cycl_def VALUE={ui_input_to_cycl_def}
564 |
565 | #fixme? enable the following?
566 | # disallow a 0 value here, force to 1 if 0 is passed?
567 | # {% if svv.ui_input_to_cycl_def == 0 %}
568 | # SAVE_VARIABLE VARIABLE=ui_input_to_cycl_def VALUE={ui_input_to_cycl_def}
569 | # {% endif %}
570 |
571 | {% endif %}
572 |
573 | # store temp targets at entry, if planning to timeout the HE/Bed
574 | {% set ui_entry_he_temp = svv.extruder_temp_setting %}
575 | {% set ui_entry_bed_temp = svv.heater_bed_temp_setting %}
576 |
577 | # catch klipper printer state on entry for conditionals below, when implemented.
578 | {% if printer.idle_timeout.state == "Printing" %}
579 | # force a pause
580 | # set timeouts for HE and Bed
581 | {% endif %}
582 |
583 | {% set ui_input_prompt = params.PROMPT|default(printer["gcode_macro _ui_vars"].ui_input_def_param_prompt) %}
584 | SAVE_VARIABLE VARIABLE=ui_input_prompt VALUE='"{ui_input_prompt}"'
585 |
586 | {% set ui_input_rcvr_macro = params.RCVR_MACRO|default(printer["gcode_macro _ui_vars"].ui_input_def_param_rcvr_macro) %}
587 | SAVE_VARIABLE VARIABLE=ui_input_rcvr_macro VALUE='"{ui_input_rcvr_macro}"'
588 |
589 | {% set ui_input_type = params.TYPE|default(printer["gcode_macro _ui_vars"].ui_input_def_param_type) %}
590 |
591 | # allow for abbreviations - int/integer, str/string, flt/float
592 | {% if ui_input_type|upper == "STR" %}
593 | {% set ui_input_type = "STRING" %}
594 | {% elif ui_input_type|upper == "INT" %}
595 | {% set ui_input_type = "INTEGER" %}
596 | {% elif ui_input_type|upper == "FLT" %}
597 | {% set ui_input_type = "FLOAT" %}
598 | {% endif %}
599 |
600 | SAVE_VARIABLE VARIABLE=ui_input_type VALUE='"{ui_input_type|upper}"'
601 |
602 | {% set ui_input_to_period = params.TO_PERIOD|default(printer["gcode_macro _ui_vars"].ui_input_def_param_to_period)|int %}
603 | SAVE_VARIABLE VARIABLE=ui_input_to_period VALUE={ui_input_to_period}
604 |
605 | {% set ui_input_excpt_hdlr = params.EXCPT_HDLR|default(printer["gcode_macro _ui_vars"].ui_input_def_param_excpt_hdlr) %}
606 | SAVE_VARIABLE VARIABLE=ui_input_excpt_hdlr VALUE='"{ui_input_excpt_hdlr}"'
607 |
608 | {% set ui_input_to_resp_def = params.TO_RESP_DEF|default(printer["gcode_macro _ui_vars"].ui_input_def_param_to_resp_def) %}
609 | SAVE_VARIABLE VARIABLE=ui_input_to_resp_def VALUE='"{ui_input_to_resp_def}"'
610 |
611 | {% if ui_input_type|upper == "STRING" %}
612 | {% set ui_dflt_bnd_lo = printer["gcode_macro _ui_vars"].ui_input_def_param_bounds_lo_str %}
613 | {% set ui_dflt_bnd_hi = printer["gcode_macro _ui_vars"].ui_input_def_param_bounds_hi_str %}
614 | {% else %}
615 | {% set ui_dflt_bnd_lo = printer["gcode_macro _ui_vars"].ui_input_def_param_bounds_lo_num %}
616 | {% set ui_dflt_bnd_hi = printer["gcode_macro _ui_vars"].ui_input_def_param_bounds_hi_num %}
617 | {% endif %}
618 |
619 | {% set ui_input_bounds_lo = params.BOUNDS_LO|default(ui_dflt_bnd_lo) %}
620 | SAVE_VARIABLE VARIABLE=ui_input_bounds_lo VALUE={ui_input_bounds_lo}
621 |
622 | {% set ui_input_bounds_hi = params.BOUNDS_HI|default(ui_dflt_bnd_hi) %}
623 | SAVE_VARIABLE VARIABLE=ui_input_bounds_hi VALUE={ui_input_bounds_hi}
624 |
625 | {% set ui_input_rmdr_period = params.RMDR_PERIOD|default(printer["gcode_macro _ui_vars"].ui_input_def_param_rmdr_period) %}
626 | SAVE_VARIABLE VARIABLE=ui_input_rmdr_period VALUE={ui_input_rmdr_period}
627 |
628 | {% set ui_input_macro_rawparams = rawparams|replace("\"","\\\"") %}
629 | SAVE_VARIABLE VARIABLE=ui_input_macro_rawparams VALUE='"{ui_input_macro_rawparams}"'
630 |
631 | SAVE_VARIABLE VARIABLE=ui_input_rcvr_macro VALUE='"{ui_input_rcvr_macro}"'
632 |
633 | {% if printer["gcode_macro _ui_vars"].ui_enable_input_hints %}
634 |
635 | {% if ui_input_type|upper == "STRING" %}
636 | {% set hint_type = "String" %}
637 | {% set ui_hint = ui_input_bounds_lo|float|round(0, 'floor')|int ~ " <= length <= " ~ ui_input_bounds_hi|float|round(0, 'ceil')|int %}
638 | {% elif ui_input_type|upper == "INTEGER" %}
639 | {% set hint_type = "Integer" %}
640 | {% set ui_hint = ui_input_bounds_lo|float|round(0, 'floor')|int ~ " <= value <= " ~ ui_input_bounds_hi|float|round(0, 'ceil')|int %}
641 | {% else %} # float
642 | {% set hint_type = "Float" %}
643 | {% set ui_hint = ui_input_bounds_lo|float ~ " <= value <= " ~ ui_input_bounds_hi|float %}
644 | {% endif %}
645 |
646 | {% set emission = "-------------------------------
" ~
647 | "" ~ hint_type ~ " input needed (w/in " ~ ui_input_to_period ~ "s): " ~ ui_hint ~ "
" ~
648 | "" ~ ui_input_prompt ~ "
" ~
649 | "-------------------------------
" %}
650 |
651 | M118 {emission}
652 |
653 | {% else %}
654 |
655 | M118 { "-------------------------------
" ~ ui_input_prompt ~ "
-------------------------------
" }
656 |
657 | {% endif %}
658 |
659 | # likely need to pause and then set timeouts for HE and Bed, or if flagged to timeout,
660 |
661 | _start_ui_timeout_watchdog DURATION={ui_input_to_period} # set input timeout watchdog
662 | _start_ui_input_loop # start input loop immediately
663 |
664 | {% if printer["gcode_macro _ui_vars"].ui_reminder_enable %}
665 | _start_ui_reminder
666 | {% endif %}
667 |
668 | # do we need to block or non_blocking_stall here???
669 | # or not block but rather carry on???
670 |
671 | _proc_end function=get_user_input
672 |
673 | # disable_code_trace
674 |
675 | #--------------------------------------------------------------------
676 |
677 | [gcode_macro _validate_user_input] # performs tests on input received to flag it as good or bad
678 | description: does simple bounds checking based on passed TYPE requirements
679 | # the following user input bounds parameters are stored in svv
680 | # ui_input_type - def to string (used to for string inputs as well as buttons)
681 | # ui_input_bounds_lo - for int/flt, lowest value to get, for str, lowest # of characters - def to 1
682 | # ui_input_bounds_hi - for int/flt, highest value to get, for str, highest # of characters - def to 255
683 | # ui_input_excpt_hdlr - bad input is reacted to in the same manner as a timeout, with a flag set: svv.ui_bad_input
684 | # ui_input_to_cycl_def - timeout iterations before resp_def sent to rcvr_macro
685 | # ui_input_to_resp_def - if ui_input_to_cycl_def is > 0 then this is passed on final timeout
686 | gcode:
687 |
688 | _proc_start function=_validate_user_input func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
689 |
690 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
691 |
692 | {% if svv.ui_input_type|upper == "STRING"
693 | and svv.ui_user_input_string != ""
694 | and svv.ui_user_input_string|string|length >= svv.ui_input_bounds_lo|round(0, 'floor')
695 | and svv.ui_user_input_string|string|length <= svv.ui_input_bounds_hi|round(0, 'ceil') %}
696 |
697 | _annunciate_good_input
698 |
699 | {% elif svv.ui_input_type|upper == "INTEGER"
700 | and svv.ui_user_input_integer >= svv.ui_input_bounds_lo|round(0, 'floor')|int
701 | and svv.ui_user_input_integer <= svv.ui_input_bounds_hi|round(0, 'ceil')|int %}
702 |
703 | _annunciate_good_input
704 |
705 | {% elif svv.ui_input_type|upper == "FLOAT"
706 | and svv.ui_user_input_float >= svv.ui_input_bounds_lo|float
707 | and svv.ui_user_input_float <= svv.ui_input_bounds_hi|float %}
708 |
709 | _annunciate_good_input
710 |
711 | {% else %}
712 |
713 | # if we get here, the input did not pass simple bounds checks - so initiate an 'exception'
714 |
715 | # first stop stuffs so toes don't get stepped on
716 | _stop_ui_input_loop
717 | _stop_ui_timeout_watchdog
718 | _stop_ui_reminder
719 |
720 | SAVE_VARIABLE VARIABLE=ui_bad_input VALUE=1
721 |
722 | # call the to exception handler proc - codified below to default to the _ui_exception_handler macro
723 | {svv.ui_input_excpt_hdlr}
724 |
725 | {% endif %}
726 |
727 | _proc_end function=_validate_user_input
728 |
729 | #--------------------------------------------------------------------
730 |
731 | [gcode_macro _process_user_input] # processes user input as either faulty (for requery), or passes onto receiver macro
732 | description: had to split this out of _await_user_input for svv.ui_bad_input visibility purposes
733 | gcode:
734 |
735 | # enable_code_trace
736 |
737 | _proc_start function=_process_user_input func_params={rawparams|string}
738 |
739 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
740 |
741 | {% if svv.ui_bad_input != 0 %}
742 | # bad input received, so need to re-call get_user_input macro which
743 | # clears cache & resets the bad input flag) so no endless loop concerns
744 | get_user_input {svv.ui_input_macro_rawparams}
745 | {% else %}
746 | # input is not bad, so need to process it as good input, calling the
747 | # RCVR_MACRO proc passed by name, passing UI_INPUT as a parameter,
748 | # wrapping in quotes if needed, and escaping embedded quotes
749 | {% if params.IS_STRING|default(0)|int == 0 %}
750 | {svv.ui_input_rcvr_macro} UI_INPUT={params.UI_INPUT} # numerical input
751 | {% else %} # if a string was entered, make sure to stringify it and escape any embedded quote marks
752 | # {svv.ui_input_rcvr_macro} UI_INPUT='"{params.UI_INPUT}"' # string input
753 | {svv.ui_input_rcvr_macro} UI_INPUT='"{params.UI_INPUT|replace("\"","\\\"")}"'
754 | {% endif %}
755 | {% endif %}
756 |
757 | _proc_end function=_process_user_input
758 |
759 | # disable_code_trace
760 |
761 | #--------------------------------------------------------------------
762 |
763 | [gcode_macro _ui_exception_handler] # default exception handler macro - can be used as a template for other user input exception handlers
764 | description: default macro called in the event of a user input timeout or out of bounds/bad user entry
765 | gcode:
766 |
767 | _proc_start function=_ui_exception_handler func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
768 |
769 | _annunciate_input_exception
770 |
771 | {% set svv = printer.save_variables.variables %} # set context for save_variables object
772 |
773 | {% set exception_hint = "Exception - " %}
774 |
775 | {% if printer["gcode_macro _ui_vars"].ui_disable_exception_hints == 0 %}
776 |
777 | {% if svv.ui_bad_input != 0 %}
778 | {% set exception_hint = exception_hint ~ "No/Bad Input" %}
779 | {% else %}
780 | {% set exception_hint = exception_hint ~ svv.ui_input_to_period ~ "s timeout" %}
781 | {% endif %}
782 |
783 | {% set exception_hint = exception_hint ~ " - expecting " ~ svv.ui_input_type ~ " " %}
784 |
785 | {% if svv.ui_input_type|upper == "STRING" %}
786 | {% set exception_hint = exception_hint ~ "of " ~ svv.ui_input_bounds_lo|round(0, 'floor')|int ~ " to " ~ svv.ui_input_bounds_hi|round(0, 'ceil')|int ~ " chars." %}
787 | {% elif svv.ui_input_type|upper == "INTEGER" %}
788 | {% set exception_hint = exception_hint ~ "between " ~ svv.ui_input_bounds_lo|round(0, 'floor')|int ~ " to " ~ svv.ui_input_bounds_hi|round(0, 'ceil')|int ~ " (inclusive)." %}
789 | {% else %} # float
790 | {% set exception_hint = exception_hint ~ "between " ~ svv.ui_input_bounds_lo|float ~ " to " ~ svv.ui_input_bounds_hi|float ~ " (inclusive)." %}
791 | {% endif %}
792 |
793 | {% set exception_hint = exception_hint ~ " " %}
794 |
795 | {% endif %}
796 |
797 | {% set exception_hint = exception_hint ~ "Please try again." %}
798 |
799 | M118 { exception_hint }
800 |
801 | {% if svv.ui_bad_input != 0 %}
802 | # so we are in a bad input state, so just return and let the calling proc react to bad input
803 | {% else %}
804 | # so this is a timeout exception and not a bad input, thus not a validation exception
805 | # so go ahead and re-call again to give user another opportunity to give the input
806 | # the REQUERY=1 is important to carry over to user exception handlers so that default timeout code works like it should
807 | get_user_input REQUERY=1 {svv.ui_input_macro_rawparams}
808 | {% endif %}
809 |
810 | _proc_end function=_ui_exception_handler
811 |
812 | #--------------------------------------------------------------------
813 |
814 | [gcode_macro _annunciate_good_input] # audible feedback of an acceptable input
815 | # also stops reminders and recursive loops
816 | gcode:
817 |
818 | _proc_start function=_annunciate_good_input func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
819 |
820 | M3002 S2000
821 | M3002 S3000
822 | M300.1
823 |
824 | _proc_end function=_annunciate_good_input
825 |
826 | #--------------------------------------------------------------------
827 |
828 | [gcode_macro _annunciate_input_exception] # audible feedback of an unacceptable input
829 | gcode:
830 |
831 | _proc_start function=_annunciate_input_exception func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
832 |
833 | M300 P750 S350
834 |
835 | _proc_end function=_annunciate_input_exception
836 |
837 | #--------------------------------------------------------------------
838 |
839 | [gcode_macro _test_show_user_input] # default receiver macro - just echos user input to console...
840 | description: test macro to display passed user input, if any
841 | #UI_INPUT
842 | gcode:
843 | _proc_start function=_test_show_user_input func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
844 |
845 | {% set ui_input = params.UI_INPUT|default("Um, NO UI_INPUT passed.!. Why? How?") %}
846 | M118 {"Received Input: " ~ ui_input ~ "
"}
847 |
848 | _proc_end function=_test_show_user_input
849 |
850 | #--------------------------------------------------------------------
851 |
852 | [gcode_macro _ui_clear_cache] # Inits all ui_ vars in the svv variable file - fires at module start, and with a new query
853 | description: helper proc to wipe/initialize svv contents related to user input
854 | gcode:
855 | _proc_start function=_ui_clear_cache func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
856 |
857 | SAVE_VARIABLE VARIABLE=ui_bad_input VALUE=0
858 |
859 | SAVE_VARIABLE VARIABLE=ui_click_up VALUE=0
860 | SAVE_VARIABLE VARIABLE=ui_click_dn VALUE=0
861 | SAVE_VARIABLE VARIABLE=ui_click_lt VALUE=0
862 | SAVE_VARIABLE VARIABLE=ui_click_rt VALUE=0
863 | SAVE_VARIABLE VARIABLE=ui_click_yes VALUE=0
864 | SAVE_VARIABLE VARIABLE=ui_click_no VALUE=0
865 | SAVE_VARIABLE VARIABLE=ui_click_conf VALUE=0
866 | SAVE_VARIABLE VARIABLE=ui_click_canx VALUE=0
867 |
868 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='""'
869 | SAVE_VARIABLE VARIABLE=ui_user_input_integer VALUE=-9999999
870 | SAVE_VARIABLE VARIABLE=ui_user_input_float VALUE=-9999999.99
871 | SAVE_VARIABLE VARIABLE=ui_user_input_raw VALUE='""'
872 |
873 | SAVE_VARIABLE VARIABLE=ui_input_prompt VALUE='""'
874 | SAVE_VARIABLE VARIABLE=ui_input_type VALUE='""'
875 | SAVE_VARIABLE VARIABLE=ui_input_bounds_lo VALUE=-9999999
876 | SAVE_VARIABLE VARIABLE=ui_input_bounds_hi VALUE=9999999
877 | SAVE_VARIABLE VARIABLE=ui_input_rcvr_macro VALUE='""'
878 | SAVE_VARIABLE VARIABLE=ui_input_to_period VALUE=0
879 | SAVE_VARIABLE VARIABLE=ui_input_excpt_hdlr VALUE='""'
880 |
881 | ## SAVE_VARIABLE VARIABLE=ui_input_to_cycl_def VALUE=-1 Dealing with this separately as it needs to survive across requeries
882 | SAVE_VARIABLE VARIABLE=ui_input_to_resp_def VALUE='""'
883 |
884 | SAVE_VARIABLE VARIABLE=ui_input_macro_rawparams VALUE='""'
885 | SAVE_VARIABLE VARIABLE=ui_input_delayed_ui_params VALUE='""'
886 |
887 | _proc_end function=_ui_clear_cache
888 |
889 | #--------------------------------------------------------------------
890 |
891 | [gcode_macro UP] # for UI button - sets UP button stroked context in svv variables
892 | description: up button for user to stroke
893 | gcode:
894 | _proc_start function=UP func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
895 | SAVE_VARIABLE VARIABLE=ui_click_up VALUE=1
896 |
897 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
898 | say_wait S="Up"
899 | {% endif %}
900 |
901 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"UP"'
902 | _proc_end function=UP
903 |
904 | #--------------------------------------------------------------------
905 |
906 | [gcode_macro DN] # for UI button - sets DN button stroked context in svv variables
907 | description: dn button for user to stroke
908 | gcode:
909 | _proc_start function=DN func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
910 | SAVE_VARIABLE VARIABLE=ui_click_dn VALUE=1
911 |
912 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
913 | say_wait S="Down"
914 | {% endif %}
915 |
916 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"DN"'
917 | _proc_end function=DN
918 |
919 | #--------------------------------------------------------------------
920 |
921 | [gcode_macro LT] # for UI button - sets LT button stroked context in svv variables
922 | description: LT button for user to stroke
923 | gcode:
924 | _proc_start function=LT func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
925 | SAVE_VARIABLE VARIABLE=ui_click_lt VALUE=1
926 |
927 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
928 | say_wait S="Left"
929 | {% endif %}
930 |
931 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"LT"'
932 | _proc_end function=LT
933 |
934 | #--------------------------------------------------------------------
935 |
936 | [gcode_macro RT] # for UI button - sets RT button stroked context in svv variables
937 | description: rt button for user to stroke
938 | gcode:
939 | _proc_start function=RT func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
940 | SAVE_VARIABLE VARIABLE=ui_click_rt VALUE=1
941 |
942 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
943 | say_wait S="Right"
944 | {% endif %}
945 |
946 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"RT"'
947 | _proc_end function=RT
948 |
949 | #--------------------------------------------------------------------
950 |
951 | [gcode_macro YES] # for UI button - sets YES button stroked context in svv variables
952 | description: yes button for user to stroke
953 | gcode:
954 | _proc_start function=YES func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
955 | SAVE_VARIABLE VARIABLE=ui_click_yes VALUE=1
956 |
957 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
958 | say_wait S="Yes"
959 | {% endif %}
960 |
961 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"YES"'
962 | _proc_end function=YES
963 |
964 | #--------------------------------------------------------------------
965 |
966 | [gcode_macro NO] # for UI button - sets NO button stroked context in svv variables
967 | description: no button for user to stroke
968 | gcode:
969 | _proc_start function=NO func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
970 | SAVE_VARIABLE VARIABLE=ui_click_no VALUE=1
971 |
972 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
973 | say_wait S="No"
974 | {% endif %}
975 |
976 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"NO"'
977 | _proc_end function=NO
978 |
979 | #--------------------------------------------------------------------
980 |
981 | [gcode_macro CONFIRM] # for UI button - sets CONFIRM button stroked context in svv variables
982 | description: confirm button for user to stroke
983 | gcode:
984 |
985 | _proc_start function=CONFIRM func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
986 | SAVE_VARIABLE VARIABLE=ui_click_conf VALUE=1
987 |
988 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
989 | say_wait S="Confirm"
990 | {% endif %}
991 |
992 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"CONFIRM"'
993 | _proc_end function=CONFIRM
994 |
995 | #--------------------------------------------------------------------
996 |
997 | [gcode_macro CANCEL] # for UI button - sets CANCEL button stroked context in svv variables
998 | description: cancel button for user to stroke
999 | gcode:
1000 |
1001 | _proc_start function=CANCEL func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
1002 | SAVE_VARIABLE VARIABLE=ui_click_canx VALUE=1
1003 |
1004 | {% if printer["gcode_macro _ui_vars"].ui_button_readback_flag %}
1005 | say_wait S="Cancel"
1006 | {% endif %}
1007 |
1008 | SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"CANCEL"'
1009 | _proc_end function=CANCEL
1010 |
1011 | #--------------------------------------------------------------------
1012 |
1013 | [gcode_macro USER_ENTRY] # for UI button macro for user to enter integer/float numbers and QUOTED strings
1014 | description: macro for user to enter integer/float numbers and QUOTED strings
1015 | gcode:
1016 |
1017 | _proc_start function=USER_ENTRY func_params='"{rawparams|string|replace("\"","`")|replace("'","`")}"'
1018 |
1019 | {% set ui_user_input_string = params.QUOTED_STRING|default("") %}
1020 | {% set ui_user_input_float = params.FLOAT|default(-9999999.99)|float %}
1021 | {% set ui_user_input_integer = params.INTEGER|default(-9999999)|int %}
1022 |
1023 | {% set ui_user_input_raw = rawparams|replace("\"","\\\"") %}
1024 | SAVE_VARIABLE VARIABLE=ui_user_input_raw VALUE='"{ui_user_input_raw}"'
1025 |
1026 | {% if ui_user_input_string != "" %}
1027 | #SAVE_VARIABLE VARIABLE=ui_user_input_string VALUE='"{ui_user_input_string|replace("\"","\\\"")}"'
1028 | #wrappered function had a specific behavior of not saving stringified numbers as strings, by design
1029 | #thus disabling and forcing natural behavior by calling organic klipper function directly
1030 | _SAVE_VARIABLE_STOCK VARIABLE=ui_user_input_string VALUE='"{ui_user_input_string|replace("\"","\\\"")}"'
1031 |
1032 | {% if printer["gcode_macro _ui_vars"].ui_input_readback_flag %}
1033 | say_wait S="You entered String value of: {ui_user_input_string|replace("\"","\\\"")}"
1034 | {% endif %}
1035 | {% endif %}
1036 |
1037 | {% if ui_user_input_integer != -9999999 %}
1038 | SAVE_VARIABLE VARIABLE=ui_user_input_integer VALUE={ui_user_input_integer}
1039 | {% if printer["gcode_macro _ui_vars"].ui_input_readback_flag %}
1040 | say_wait S="You entered Integer value of {ui_user_input_integer}"
1041 | {% endif %}
1042 | {% endif %}
1043 |
1044 | {% if ui_user_input_float != -9999999.99 %}
1045 | SAVE_VARIABLE VARIABLE=ui_user_input_float VALUE={ui_user_input_float}
1046 | {% if printer["gcode_macro _ui_vars"].ui_input_readback_flag %}
1047 | say_wait S="You entered Float value of {ui_user_input_float}"
1048 | {% endif %}
1049 | {% endif %}
1050 |
1051 | _proc_end function=USER_ENTRY
1052 |
1053 | #--------------------------------------------------------------------
1054 |
1055 | [gcode_macro _null] # may have been for dev testing - not currently used, I don't believe - may nix this...
1056 | description: UNTESTED Dummy macro to have be a null receiver - just saves UI_INPUT to svv
1057 | gcode:
1058 |
1059 | _proc_start function=_null func_params='"{rawparams|string|replace("\\\"", "")|replace("\\\'", "")|replace("\"", "")|replace("\'", "")}"'
1060 |
1061 | {% set ui_last_input = params.UI_INPUT|replace("\"","\\\"") %}
1062 | SAVE_VARIABLE VARIABLE=ui_last_input VALUE='"{ui_last_input}"'
1063 |
1064 | _proc_end function=_null
1065 |
--------------------------------------------------------------------------------
/pics/MacroTitles&Comments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TodWulff/Klipper_UserInteraction/a6b427c23d5bf60c01d5aae0878c6c594abb4748/pics/MacroTitles&Comments.png
--------------------------------------------------------------------------------