I need to control my printer(s) with a wireless keyboard. I can plug the keyboard into the host port of the PI and see it in Debian.
I need to:-
Prevent any other application including the Debian console from getting the usb input from that particular device (and that device only).
Decode the Usb HID information to pipe it to the OctoPrint server (in the backend)
Have the server send the relevant command to Marlin (and I might have to modify Marlin 2.0) to perform the required task.
I already know that octoprint's web interface can accept keyboard input but I want to map those functions (as is) to a physical keyboard that is plugged in.
Can anyone help with any of those 3 requirements. I'll be publishing my solution, assuming it's practical, to git hub for all to use afterwards.
This only works with a compatible browser and the control tab active. So it's still really being controlled by the remote client, not a local keyboard on the pi.
I think in order to do what you're proposing you would have to run a background service of some kind to middle-man the keyboard input. You might be able to develop something using pynput or something similar. I've never worked with this library, just found it doing a cursory search.
I haven't tried it yet but the way I understand it you are redirecting the keyboard input to the script in the command and turning off the input to the console. I will preface this with the fact that I'm not a linux guy and that might break something else. I was able to put together a python script that would echo back the keyboard input using the keyboard module.
Ah, but the objective isn't to remove every keyboard's input altogether, just a specific device. I can see the data that comes from it and it's possible to hijack it so that nothing else can access it. But I am sure that some linux person out there has a process that can decode usb hid keyboard events and spit out some key down and key up events for me. I should be able to pipe that to OctoPrint somehow..
@foosel Is it proper to have an Octoprint plugin that installs itself as a service on the OctoPI?
I want to try and make a plugin that installes the Dotnet Core 3.0 framework, then downloads the code from git hub, compiles it and installs itself as a service. It will then communicate with OctoPrint via the API;
Since that involves changes outside of OctoPrint, no. Any such changes should at the very least involve asking the user, and preferably prompt them to do the required steps (e.g. via a provided installer script). See also the plugin registration checklist:
Your plugin doesnโt attempt to modify the userโs system without their knowledge, e.g. by trying to install additional system packages, services or the like. If your plugin needs additional steps like this to function, add a wizard dialog that prompts the user to do these things, do not do them automatically.
Exception: Fetching additional Python dependencies from the Python Package Index through plugin_requires in your setup.py is fine.
Ok then I should do this as being totally independent to OctoPrint and just mention that it can control OctoPrint even though the configuration interface might be in OctoPrint.
xinput list
# Note the ID of the keyboard in question
xinput float 13 # For example, if the ID was 13
# This should stop this keyboard's input from being directed to the window with keyboard focus
Create a udev rule so that a non-root can read from it (noting that you'd need to adjust for your device's idVendor and idProduct - see dmesg to find this out):
Yeah, I think the pynput module I originally posted also requires that. However, I think the keyboard module has a means somehow to not if ran with the sudo command and therefore possible when run as a service, but again I haven't tested this myself. I may do some testing this weekend on it to see if it works or not. From the perspective of killing the keyboard input to the rest of the system, why would that be an issue. From what I was reading in the post "Redirect physical keyboard input to SSH" linked above you could still SSH to the device separately and still run commands etc, and even use the screen command to monitor what keyboard input/entry was doing.
I found a utility called usbhid-dump. I think that if I can get a python wrapper around it, I might be able to make a workable solution. (Unfortunately I don't know python ) Time to learn I guess.
How do I test python scripts in OctoPrint anyways?
EDIT: Crap this thing requires root privileges. Does OctoPrint run with root priv? (i'm guessing not)
Look in the /etc/sudoers.d folder for some files which alter the instances in which the pi user, for example, can do password-less sudo. (Go and do likewise.)
usbhid-dump : is the program that that allows for listening to the keyboard.
-d 248a:8367 : is a filter for a specified device by manufacture and product id. If omitted then every keyboard will produce events.
-es -t 0 : start capture, indefinitely.
| egrep '^\s[0-9A-F]' : from that output, filter out the data that we need
which produces the output:
00 00 04 00 00 00 00 00
00 00 00 00 00 00 00 00
From which I've determined that this is the letter A key being depressed. Only the last six hexes are keys being depressed. If there is more than one key then the last six will produce non zero hexes.
This is from the usb 1.1 hid spec (https://www.usb.org/sites/default/files/documents/hid1_11.pdf pg73)
While I understand what's going on in "printer.py" conceptually, how do I call printer.jog from an external python script? (assuming I use a python script to box up all this stuff)
EDIT: Or would a plugin have "access" to call the printer.jog function internally?
A plugin would have access to the injected property self._printer that would have access to all the commands. One of them being jog, basically any of the functions listed at the link below.
Ok I managed to write a python script to detect the key presses:
# coding: utf-8
import subprocess
import re
usbDump = subprocess.Popen(["usbhid-dump", "-d 248a:8367","-es","-t 10000"], stdout = subprocess.PIPE)
checkForKeys ={
0x50: "โ",
0x52: "โ",
0x4F: "โ",
0x51: "โ",
0x28: "Ok"
}
try:
while True:
m = re.search("(\s[0-9A-F]{2}){8}", usbDump.stdout.readline())
if m :
keyData = m.group(0).split()
keyData = [int(i,16) for i in keyData]
for i in range(len(keyData)):
if keyData[i] in list(checkForKeys):
print(checkForKeys.get(keyData[i], ""))
finally:
usbDump.kill()
Before I try and turn this into a plugin, does it all look sorta sane?
Check to make sure that the keypresses are the same if the user has the CAPSLOCK toggled to ON (and you might try the NumLock ON/OFF as well). I don't know if readline() is well-behaved but you might want to add a little delay in there inside that loop to allow some breathing room.
Yes the key presses would be the same. The keyboard doesn't not control capitalization; the host does. the usb hid spec only specifies the key numbers that are depressed. In fact if you press caps lock, it is up to the host to send back a message to turn the caps lock light on. Same goes for num lock and scroll lock.