Help for my plugin development 🙏

Hello Guys, :smiley:
I'm currently building my first Octoprint application. The official documentation helped me a lot, but I'm actually stuck. I try to send a simple Gcode to the terminal with a button, but nothing appears in the terminal, or in the logs... Do you have any suggestion ?

Here is my .jinja2:

<div id="control-jog-z-config" class="jog-panel">
	<h1>Z Config</h1>
	<div>
		<input class="control-label" type="text" autocomplete="off" id="command-search" data-bind="enable: loginState.isUser(), textInput: commandString" placeholder="Val"/>
			
		<button class="control-label" type="submit" class="btn" data-bind="enable: isOperational(), click: sendCommand">Start</button>
	</div>
</div>

And my .js:

/*
 * View model for Zconfig
 *
 * Author: Thierry Guyot
 * License: AGPLv3
 */
$(function() {
    function ZconfigViewModel(parameters) {
        var self = this;

        // assign the injected parameters, e.g.:
         self.loginStateViewModel = parameters[0];
         self.settingsViewModel = parameters[1];

         self.commandString = ko.observable("");
         self.cmdHistoryIdx=-1;

         self.onStartup = function() {
            $('#control-jog-z-config').insertAfter('#control-jog-general');
        }


        self.onBeforeBinding = function() {
            self.cmdHistoryIdx=self.terminal.cmdHistory.length;
        }

        self.sendCommand = function() {
            console.log("Hello SendCommand " + self.commandString());            
            var splitCommands = self.commandString().split(";");     
            var len = splitCommands.length;
            for(var i=0;i<len;i++){
                self.terminal.command(splitCommands[i]);
                self.terminal.sendCommand();
            }

            self.cmdHistoryIdx = self.terminal.cmdHistory.length;
            self.commandString("");
        };


    }

// view model class, parameters for constructor, container to bind to
    OCTOPRINT_VIEWMODELS.push({
        construct: ZconfigViewModel,
        dependencies: ["loginStateViewModel","settingsViewModel"],
        elements: [ "settings_plugin_Zconfig_fo rm", "control-jog-z-config" ]
    });
});

Thank you in advance :yum:

Personally, I use Octoprint.control.sendGcode() as opposed to self.termiral.sendCommand(). To be fair, I'm not sure what the best practice is supposed to be, but sendGcode() is working for me. You can provide either a gcode string or an array of gcode strings for it to send.

Array example:

Single String example:

1 Like

This is definitely the way to go. I use this a lot with many of my plugins.

1 Like

Hello @cilynx @jneilliii :grinning:

Thank you for your answer. I modify my .js, but the command seems not working, even with the Octoprint.control.sendGcode()

Is my .jinja2 correct ?

Thank you very much guys

Your element binding are probably wrong. They need to be valid jQuery selectors.

Here is my new .js:

/*
 * View model for Zconfig
 *
 * Author: Thierry Guyot
 * License: AGPLv3
 */
$(function() {
    function ZconfigViewModel(parameters) {
        var self = this;

        // assign the injected parameters, e.g.:
         self.loginStateViewModel = parameters[0];
         self.settingsViewModel = parameters[1];

         self.commandString = ko.observable("");
         self.cmdHistoryIdx=-1;

         self.onStartup = function() {
            $('#control-jog-z-config').insertAfter('#control-jog-general');
        }

         self.sendCommand = function() {
            OctoPrint.control.sendGcode("M400 G38.3 X+100 G91 G0 X-1");
        }
    };

// view model class, parameters for constructor, container to bind to
    OCTOPRINT_VIEWMODELS.push({
        construct: ZconfigViewModel,
        dependencies: ["loginStateViewModel","settingsViewModel"],
        elements: [ "settings_plugin_Zconfig_form", "control-jog-z-config" ]
    });
});

And my .jinja2:

<div id="control-jog-z-config" class="jog-panel">
	<h1>Z Config</h1>
	<div>
		<input class="control-label" type="text" autocomplete="off" id="command-search" data-bind="enable: loginState.isUser(), textInput: commandString" placeholder="Val"/>
			
		<button class="control-label" type="submit" class="btn" data-bind="enable: isOperational(), click: sendCommand">Start</button>
	</div>
</div>

Change this

to this

elements: [ "#settings_plugin_Zconfig_form", "#control-jog-z-config" ]

and you may have to update your get_template_config in __init__.py to be custom_binding = True.

It's not working... It will drive me crazy !!!! :exploding_head:

Can you share a zip of your entire plugin folder and I can take a look from here?

Yes, of curse: Direct link to my Github project

give me a couple of minutes...I think I know what it is but want to verify.

No problem !!! I've been stuck on it since 2 weeks, I can wait some minutes :rofl:
Thank you so much !

Pull Request submitted to your repo. It was basically just a binding scope issue. When you inject stuff into other view models you have to make sure that you are also injecting your observables and functions into that view model you're injecting into.

There are other ways to do what you're attempting though, for example using the getAdditionalControls callback in javascript will do the UI injection for you and has a lot of power and flexibility. You may want to take a look at that as an option as well. It follows the same rules as Custom Controls, so parameters, parsing return values, etc.

For an example of the getAddiiontalControls callback you can take a look at my BLTouch plugin. And here is the docs on everything that you can do with custom controls.

https://docs.octoprint.org/en/master/features/custom_controls.html#sec-features-custom-controls

1 Like

Wow; just ... Amazing ! Thank you so much !

Sure, glad to help.

Just a warning,.when doing this approach with the viewmodel injection make sure to check the receiving viewmodel and verify you're not overwriting a default functions or observables so it doesn't cause issues with internal functions, hence the rename of the function I did.