Help with AttributeError: 'FanSpeedMirror' object has no attribute 'M106command'

I have some fans connected to the Raspberry Pi via a hat and I'd like the speed of those fans to mirror the cooling fans on the printer, i.e. M106 and M107 commands.

My first implementation was to modify the plugin Fan Speed Control which was successful, but suffers from the problem that I have to modify that plugin every time there is an update to it.

I've had a request for my code so I decided to break my modifications out into my own plugin, Fan Speed Mirror. Between the plugin tutorial and my modifications to Fan Speed Control, I have created my own plugin and while there were some bugs, it started working fairly well.

As some point in the process, I started getting the error, "AttributeError: 'FanSpeedMirror' object has no attribute 'M106command'" and I can't figure out what I've done wrong since it was working at one time.

I've attached a zip file of the current state of the plugin and hopefully someone can see what I've done wrong.

I'm a bit reluctant to create the github repository until I have something that works, but I can do that if it would make getting help easier.

OctoPrint-FanSpeedMirror-1.0.0.zip (13.7 KB)

octoprint.log (24.1 KB)

I installed your code under 1.3.11rc2 and 1.3.11rc3.dev and it works. No exception here.

If it were me, I'd insert at line 14:

  M106command = ""
  M107command = ""

I'm not sure why you have a comma at the end of your commented-out line 17, btw.

I would guess "startup race condition" as the cause. Or you could call it "chicken and egg". You want to refer to something that sometimes hasn't yet been created. At one time, you probably had some of that __init__() function working.

Thanks! I've got it working now.

I think it was a combination of the "def init(self)" code being commented out (and the commented out code having the extra comma) and reverting a change to the viewmodel.

Should be available at https://github.com/b-morgan/OctoPrint-FanSpeedMirror. I'm still working on the "documentation" and the public submission part.

1 Like

foosel might over-rule me on this one, but if I were to use init in my class I think I would then adjust like...

	def __init__(self, **kwargs):
		super(FanSpeedMirror, self).__init__(**kwargs)
		self.M106command=""
		self.M107command=""

She might even enlighten us on what this does (or what happens if you don't). Personally, I would guess that the kwargs part allows the list of key=var's to be fed back into the class(es) that this was derived from ("super" then being like an alias to the parent class). It might be like "run the parent class's initialization function with this object's self and list of named variables".

I've found that this is required sometimes. And by sometimes, I mean if you're creating your own instance of an onscreen keyboard and you still need the original class's behavior to fall back upon.

I'm guessing here:

Back into OctoPrint, your main class receives things like these goodies. I'm guessing that the "super" init in question is the one on line 96. So—maybe—if you don't super that then you don't get the goodies (97-108).

Nope. I didn't want to enforce particular constructors on people (this is what __init__ is) and the injection is thus done using getattr during plugin initialization (before initialize is called). A super call isn't necessary and I've in fact seen problems caused by it during specific plugin reload scenarios. That was back in 2014 though when I initially built the plugin system and I can't remember the details.

If you are interested into this kind of stuff and how it works together, do a deep dive into src/octoprint/plugin/core.py.

1 Like

Of course I'm interested in all this stuff. :smile:

@OutsourcedGuru @foosel Please don't confuse me with the facts :shushing_face:

I have one more question, the text input fields defined in the fanspeedmirror_settings.jinja2 are too narrow (i.e. you have to scroll left and right to see the complete command entered). Can you point me to something that will tell me how to make the box wider?

Take a look at the input sizing classes documented here

https://getbootstrap.com/2.3.2/base-css.html#forms

The old-school way would have been inline:

<input size="30" type="text" class="input-block-level" data-bind="value: settings.plugins.FanSpeedMirror.M106command">

I might still do this temporarily in the Developer's console to figure out how big I should make it.

The new, expected way would be to use your CSS file. So that you don't accidentally style someone else's text input box, you probably want to add your own namespaced class to it.

<input class="input-block-level FanSpeedMirror-input" data-bind="value: settings.plugins.FanSpeedMirror.M106command">

And then in your CSS file...

.FanSpeedMirror-input {
    width: 200px;
}

Or you could say 30em if you wanted this to be measured in characters like before. It possibly won't be necessary to be more specific than this for the CSS selector.

@foosel @OutsourcedGuru Thank you both for the answer(s). I went with @foosel's solution.

For any anthropologists who find this centuries from now...

<input class="input-mini" type="text" placeholder=".input-mini">
<input class="input-small" type="text" placeholder=".input-small">
<input class="input-medium" type="text" placeholder=".input-medium">
<input class="input-large" type="text" placeholder=".input-large">
<input class="input-xlarge" type="text" placeholder=".input-xlarge">
<input class="input-xxlarge" type="text" placeholder=".input-xxlarge">