Help with plugin development / IDE setup


#1

Hi there,

I would like to create a plugin to dim my printbed led_strip light from the octoprint control tab.
I looked at the already existing plugins and found the fanslider plugin from @ntoff looking like a simple and elegant solution.
So I tried to reverse engineer based on the fanslider plugin a "lightslider" plugin.

My idea is to use a pwm signal from a gpio pin to control a mosfet which will then dim my led strip.
The hardware part is already setup and working like a charm with a standalone python script executed directly on the pi.

The final part would be to incorporate said script/commands into the lightslider plugin.
I found out that, in order to pass the value from the slider, I would need to use the SimpleApiPlugin. And incorporated that functionality with the help of this post https://discourse.octoprint.org/t/help-how-to-call-python-func-from-js/3728 .

After adding a variable for the settings page (wanted to make the gpio pin for the pwm user configurable) and renaming the variables (from fan -> to light) i tried my luck on a spare Rpi with a fresh octopi installation.
But the big bang integration did not go so well (as usual :roll_eyes:)
I found some typo's and mistakes, but finally got the plugin running without any error's in the octoprint.log.

Problem is, it isn't doing much...

I managed to add the slider and two buttons to the control tab, but they do not seem to do anything.
The tab in the octoprint settings plugin page is blank, no fields, no text, nothing.

I checked the naming conventions (since I renamed all variables containing "fan") as far as I could, to no avail.

Could somebody take a look at the code and see if he find's anything obvious? I got it sitting in front of me for three days, I feel like I can't see the wood for the trees.
The files can be found here: https://github.com/cutnicace/octoprint_lightintensityslider

Or help me by giving an example how to setup an IDE which can run the plugin embedded into the octoprint server. It would help me immensely if I could step through the single function call's for debugging.
I tried with the IDE Setup description in the Development section but got frustrated after a while, finding out the education version of PyCharm I installed did not correlate with the writeup. The community version on the other hand looks pretty much overloaded with features. I could replicate parts of the howto, but not to the extend of a running plugin in the octoprint server instance.

As a starting point for the code review, I am wondering if this is correct (it seems to work with the original plugin) but all other plugins I looked at had just a single "__plugin_implementation__ = pluginname()" declaration.

def __plugin_load__():
	global __plugin_implementation__
	__plugin_implementation__ = __plugin_implementation__ = FanSliderPlugin()

Also I ask myself how the layout file for the setup tab is referenced. The .css and .js files are hardcoded into the get_assets function but the .jinja2 file is just matched by the filename containing the plugin name?

Thanks in advance
Steffen


#2

I would probably look at this instead https://github.com/google/OctoPrint-LEDStripControl and perhaps just try to add a slider to that plugin, maybe a combination of that and the fan slider.

As a starting point for the code review, I am wondering if this is correct

I'll admit, that's got even me scratching my head... dunno why that's duplicated like that. Probably copy/paste gone wrong

Also I ask myself how the layout file for the setup tab is referenced. The .css and .js files are hardcoded into the get_assets function but the .jinja2 file is just matched by the filename containing the plugin name?

Maybe these will help

http://docs.octoprint.org/en/devel/plugins/mixins.html?highlight=get_assets#octoprint.plugin.AssetPlugin.get_assets

http://docs.octoprint.org/en/devel/plugins/mixins.html?highlight=get_template_configs#octoprint.plugin.TemplatePlugin.get_template_configs


#3

I think your issue might be right here. data should probably be data.percentage or data["percentage"]?


#4

@PythonAteMyPerl
Thanks, the second link answered my question about the reference.

I took a look at the plugin, but it does not really suit my application. It is meant for RBG led_strips to be controlled via gcode, I am dealing with a simple coldwhite led_strip which i want to control over the gui. From my standpoint the modifications needed for that plugin to do what I want are similar as what I need to achieve with the lightslider plugin.

@jneilliii

Thanks for the hint. You are right, but unfortunately the failure could not even cause problems as the on_api_command function is never called.
But you got me thinking and I found another mistake in the .js code which prevented the post message from being sent.

Ok, maybe I should have been a bit more specific here :wink:
I inserted some log commands into the on_after_startup as well as the on_api_command function.
In the log I can see that the plugin starts up ok and also creates the GPIO.PWM instance (which I can also validate with my multimeter at the gpio port) but after pressing the "lights out" button in the gui on_api_command is not called.
In the log I see a tornado.access - WARNING - 404 POST /api/plugin/lightslider when I send a post message via the gui buttons.

So I fired up wireshark and looked directly at the sent message which looks like this:

Hypertext Transfer Protocol POST /api/plugin/lightslider HTTP/1.1\r\n Host: #.#.#.126\r\n Connection: keep-alive\r\n Content-Length: 33\r\n [Content length: 33] Accept: application/json, text/javascript, */*; q=0.01\r\n Cache-Control: no-cache\r\n Origin: http://#.#.#.126\r\n X-Requested-With: XMLHttpRequest\r\n User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n X-Api-Key: ************************860E2\r\n Content-Type: application/json; charset=UTF-8\r\n Accept-Encoding: gzip, deflate\r\n Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7\r\n [truncated]Cookie: session_P80=********************************************* Cookie pair [truncated]: session_P80=********************************************* \r\n [Full request URI: http://#.#.#.126/api/plugin/lightslider] [HTTP request 1/2] [Response in frame: 90] [Next request in frame: 147] JavaScript Object Notation: application/json Object Member Key: "command" String value: dim Member Key: "percentage" Number value: 50

For me this looks like it should (compared with the example of a valid post request on http://docs.octoprint.org/en/master/plugins/mixins.html#simpleapiplugin)?
The response to this message is a plain "404 NOT FOUND"...

Any suggestions?


#5

oh, and I uploaded the latest code into a new folder, since I harmonized the filenames:


#6

My point was the whole GPIO control, I tried to implement it once, saw that it looks like a bunch of 3rd party services were involved from that RGB control and just gave up. You can do it with calls to shell scripts, but I'm quite weary of doing anything through octoprint that then relies on a shell script to interact with the system hardware.

When I tried messing about with GPIO, it would trigger and I'd see a blip on the scope, but then immediately turn off. It seemed like without a system level service to control the GPIO pins, python doesn't keep the pin active, it returns to its off state unless you run some sort of python script in the background constantly keeping the pin "alive".

I'll admit I have very limited knowledge on interacting with GPIO pins "remotely", only ever done it with a python script that ran locally as a python script (not part of a web app) and monitored a pin for an input. I never got it working through octoprint as a plugin.


#7

Ironically, that's one of the things that already work in the plugin...


#8

That means that the issue is with the path to the plugin url you are posting to. I think that is a result of your url path not matching your plugin identifier as loaded within the app. Since you don't have a setup.py implementation I'm not sure what the identifier will default to, but you might want to try looking into /api/plugin/<plugin identifier>/ maybe /api/plugin/lightintensityslider/?


#9

I thought so too, but I tried every combination I could think of, with no luck.

API_BASEURL + "plugin/lightslider" -> http://#.#.#.126/api/plugin/lightslider
API_BASEURL + "plugin/lightslider/" -> http://#.#.#.126/api/plugin/lightslider/
API_BASEURL + "plugin/lightsliderplugin" -> http://#.#.#.126/api/plugin/lightsliderplugin
BASEURL + "plugin/lightslider" -> http://#.#.#.126/plugin/lightslider
BASEURL + "plugin/lightslider/" -> http://#.#.#.126/plugin/lightslider/
BASEURL + "plugin/lightsliderplugin" -> http://#.#.#.126/plugin/lightsliderplugin

Always with the same result : 404

Is there anyway you can readout what octoprint "thinks" your plugins identifier is?


#10

what about /api/plugin/octoprint_lightslider. You can easily just try it in the browser to see if it gives a 404.


#11

If you look in your log file for where you are adding a self._logger.info("something") the part just after the timestamp in the log file is your identifier.

http://docs.octoprint.org/en/master/plugins/gettingstarted.html#saying-hello-how-to-make-the-plugin-actually-do-something


#12

Bingo!
That's the one.


#13

Cool, don't forget to mark the solution....


#14

Ok, I could also have thought about that one :face_with_raised_eyebrow:
I had it displayed in front of me too many times...

Ok now I only need to get the plugin settings page display anything :joy:


#15

Rename the file to octoprint_lightslider_settings.jinja2


#16

Then your going to have to go through your template and adjust all the references as they won't bind.

Like this line here will have to be adjusted to

<input type="number" min="0" max="100" class="input-mini" data-bind="attr: { title: defaultTitle }, value: settings.plugins.octoprint_lightslider.defaultLightIntensity">

#17

I might be wrong about this though.


#18

Nope, you are right about it.

!!!IT LIVES!!!

Big Thanks, to you the plugin is working like it should now!