How to validate user input?

Hi guys, is there a way to validate user input in plugin settings page? I would need to validate raspberry pin numbers are below 40. Thanks

Not sure if there is any validation built in to OctoPrint's core framework, but in cases like this I would use a number tag with a min and max property set.

<input type="number" min="1" max="40" step="1" data-bind="value: my_observable"/>

Not sure if it will block saving, but setting the error class on an input may do it as well. An example of an ip validation was removed from my Tasmota plugin, but you can see how it worked here.

1 Like

If you don't want to implement the logic via javascript you can use the on_settings_save method in your plugin:

# save current settings, do some input validation
# !!! data includes only the delta settings between the last save-action !!!
def on_settings_save(self, data):
    ..validate...
    # default save function
    octoprint.plugin.SettingsPlugin.on_settings_save(self, data)

I thought about this but couldn't find a simple way to notify end user about bad inputs

wow, this seems to work, thanks. If I were really picky could i somehow ban non-gpio pins? :smiley:

Not sure if that is possible, but what Ollis suggested would be the place where you could make sure the values were legitimate and potentially change the value before it gets saved.

I think I could use input pattern to validate the pins are GPIO but not sure every Raspberry is the same.

Hi @Luke_Malatinsky,
for user notification I used the following combination of and javascript:

	self._sendDataToClient(dict(action="errorPopUp",
								title= title,
								message=message))

JavaScript

    // receive data from server
    self.onDataUpdaterPluginMessage = function (plugin, data) {

        if (plugin != PLUGIN_ID) {
            return;
        }

        if ("errorPopUp" == data.action){
            new PNotify({
                title: 'ERROR:' + data.title,
                text: data.message,
                type: "error",
                hide: false
                });

            return;
        }

Found a way to validate pins eventually, it was more straight forward than I thought - I catch GPIO ValueError when reading input from particular pin, this shows me ground and power pins nicely.

I agree with @OllisGit that to validate the pins in on_settings_save() method is the way to go but I think that using push update when validation fails would be neater - I could maybe show the validation message right in settings page.
Do you see any reason why this couldn't work? I haven't used push updates so far, that's why I'm asking.

I've used that in several of my plugins, you could use that as an option, but not sure if would be able to block the save or not. I typically have it feeding data, or popping up a message on error using PNotify.

python side

self._plugin_manager.send_plugin_message(self._identifier, dict(error="Testing"))

javascript side:

		self.onDataUpdaterPluginMessage = function (plugin, data) {
			if (plugin !== "<put your plugin identifier here>") {
				return;
			}

			// Do stuff here like PNotify alert...
			if (data.error) {
				new PNotify({
					title: 'Error',
					text: '<div class="row-fluid"><p>Looks like your settings are not correct or there was an error.</p><p>Please see the <a href="#" target="_blank">Readme</a> for configuration tips.</p></div><p><pre style="padding-top: 5px;">'+mesh_data.error+'</pre></p>',
					hide: false,
					type: 'error'
				});
				return;
			}

Oh shoot I didn't realize that pop-ups are basically push updates, sorry guys.

So in the end I had to use pop-up message to show the user that he's saving setting with invalid pin, I couldn't show it on settings page because after pressing the save button settings go away immediately everytime, altering this would be against OctoPrint plugin policies I believe. Shame validation isn't built into OctoPrint yet.

Anyway, thanks for your help again. :slight_smile:

You could still do visual indicators of invalid entries with knockout binding css rules for bootstrap classes.