diff --git a/README.md b/README.md index b1ccfef..aa8c335 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ An OctoPrint plugin to allow USB ports to be programmatically turned OFF and ON from within the web interface. ## Overview -> **Raspberry Pi 3B**: A single-board computer made by the Raspberry Pi Foundation +> **Raspberry Pi**: A single-board computer made by the Raspberry Pi Foundation > > **USB**: (abbreviation of Universal Serial Bus) is an industry standard that establishes specifications for cables, connectors and protocols for connection, communication and power supply between personal computers and their peripheral devices. > @@ -22,11 +22,10 @@ Rather than rigging up inline switches, applying tape inside the tongue of a USB ...I thought that I would provide this plugin as a simpler alternative. -![Settings](https://user-images.githubusercontent.com/15971213/52908070-d4ae9980-3222-11e9-925a-6bff7af7badb.png) - ## Compatibility This plugin was created to support only the following platforms: +* Raspberry Pi 4B * Raspberry Pi 3B * Raspberry Pi 3B+ * Raspberry Pi 2B @@ -41,42 +40,19 @@ It is NOT intended to support the following: * Raspberry Pi Zero * PCs, Macs, generic Linux workstations *or anything else* -Also, this plugin is NOT intended to support those of you who wish to plug more than one printer into your Raspberry Pi 3B. +Also, this plugin is NOT intended to support those of you who wish to plug more than one printer into your Raspberry Pi. ## Primary use case I expect that this plugin would be useful for anyone who needs to remove power which is being sinked over to their printer's LCD panel when their printer board is powered OFF. ## Complications -### Raspberry Pi 2B/3B can't individually control ports -The `uhubctl` author informs me that internally the Raspberry Pi 2B and 3B cannot individually toggle ports. At best, the gang of four ports can be toggled as a group. - -### Raspberry Pi 3B+ - -I've just purchased and tested the Raspberry Pi 3B+ thoroughly. It seems to have an odd pairing of one internal hub in two sections. In theory, your printer would be controllable from USB2 and a second smart device from USB3. You may also toggle ALL of the Type A style of USB ports as well from the ALL slider. - -For my own printer controller board, toggling the power off prevents any communications from happening (but at least it wouldn't sink power over to it, if this is your intention). +### Raspberry Pis can't individually control ports +The `uhubctl` program that this project uses at its core is unable to toggle individual USB ports on Raspberry Pi devices. This is a limitation of the hardware, not the software. When using this plugin, you can only control *all four* USB ports at the same time. ### This won't necessarily work while printing I note that on my own test rig, the Robo 3D printer board includes a jumper to determine how the board is powered. If the jumper is set to "USB", this plugin works as expected to completely power ON/OFF all functions on the board over the USB port although this wouldn't technically work in a real printer given the power limitation. **If instead the jumper is set to "5V" then toggling the associated USB port to OFF from this plugin will result in a communication error after the timeout has been reached.** Therefore this is not compatible with the normal functioning of this board for the purpose of printing. -**It *would* be useful to stop sinking power to the board when it is powered OFF however.** - -| Popup | Seen in State side panel | -|---|---| -| ![CommunicationsError](https://user-images.githubusercontent.com/15971213/52870077-e1849d80-30fb-11e9-8924-95b889d39797.png)| ![TooManyTimeouts](https://user-images.githubusercontent.com/15971213/52870118-f8c38b00-30fb-11e9-9fd5-0ee2938f7ba4.png) | - -So in some cases, this simply won't work for printing. (A better alternative might be a USB adapter with an inline diode for the 5V line.) - -### Which port? -There are five USB ports on the USB hub known as `1-1` in the Raspberry Pi 3B. USB port 1 is the micro USB connection we usually power the board with. The remaining ports 2 through 5 are represented in the interface. - -The Raspbian operating system assigns these port numbers not by their physical location in the four-connector arrangement but by the ID of the device during bootup or when the cable is connected. - -For this reason, I a not persistently storing your settings for every session because this could get confusing if you add/remove USB devices. - -#### Raspberry Pi 3B+ -This computer has both a `1-1` and a `1-1.1` hub as seen from `lsusb`, for example. The second version has some individual control over the first two smart ports, as seen now in the Settings interface. ### Persistence At the moment, this plugin does not save the power state for each USB port. Each restart of OctoPrint will basically forget how you last left these and assume that they're all ON. @@ -108,7 +84,7 @@ When initially installed, the plugin still needs a script to be run once. The in ## REST API The plugin includes an API for programmatically toggling the ports ON/OFF in a way which is similar to OctoPrint's. You can find/copy your API key under **Settings** -> **API**. -### Controlling USB port 2 remotely +### Controlling power remotely (All except Raspberry Pi 4B) ``` POST /api/plugin/usbcontrol HTTP/1.1 Host: example.com @@ -116,19 +92,13 @@ Content-Type: application/json X-Api-Key: abcdef... { - "command": "usb2", - "arg2": "on" + "command": "all", + "argAll": "on" } ``` +Values for argAll are "on" and "off" -For USB port 2, the `arg2` options are "on" and "off". - -### Controlling USB ports 3 through 5 remotely -The API is similar for the remaining ports. Instead of "usb2" and "arg2", replace these with the appropriate number. - -#### Raspberry Pi 3B+ -There is a special case for this computer (only) for the API to allow all ports to be toggled ON/OFF. - +#### Controlling power remotely (Raspberry Pi 4B) ``` POST /api/plugin/usbcontrol HTTP/1.1 Host: example.com @@ -136,19 +106,23 @@ Content-Type: application/json X-Api-Key: abcdef... { - "command": "all", - "argAll": "on" + "command": "usb4", + "arg4": "on" } ``` +Values for arg4 are "on" and "off" + +## Update 1.0.8 +Update to support RPi4 -## Update -As of v1.0.7, the installation process of the plugin will automatically add a symlink to `/usr/local/bin` for the `uhubctl` program so that you can simply run it from the command line if you're into that. +## Update 1.0.7 +Installation process of the plugin will automatically add a symlink to `/usr/local/bin` for the `uhubctl` program so that you can simply run it from the command line if you're into that. ## Reporting bugs/issues Please do not report a bug if you *can't* print with the USB power turned OFF. It is assumed that this is the case for most printer controllers. If this does work for you, feel lucky. By the way, this means the same as "I've just gotten a **Communications Error** popup with the USB port toggled OFF". This is not meant to be a solution to this problem. -Please do not report a bug if you are plugging more than one printer into your Raspberry Pi 3B at the same time. +Please do not report a bug if you are plugging more than one printer into your Raspberry Pi at the same time. -Please do not report a bug if you are using a platform that isn't listed in the **Compatibility** section above. \ No newline at end of file +Please do not report a bug if you are using a platform that isn't listed in the **Compatibility** section above. diff --git a/setup.py b/setup.py index 4e1619a..07966f1 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ plugin_name = "USBControl" # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module -plugin_version = "1.0.7" +plugin_version = "1.0.8" # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin # module diff --git a/usbcontrol/__init__.py b/usbcontrol/__init__.py index 734815a..89463d0 100644 --- a/usbcontrol/__init__.py +++ b/usbcontrol/__init__.py @@ -17,30 +17,26 @@ class UsbcontrolPlugin(octoprint.plugin.SettingsPlugin, def get_settings_defaults(self): s = settings() return dict( - usb2 = True, - usb3 = True if s.get(["plugins", "usbcontrol", "isRaspi3Bplus"]) else False, - usb4 = True if s.get(["plugins", "usbcontrol", "isRaspi3Bplus"]) else False, - usb5 = True if s.get(["plugins", "usbcontrol", "isRaspi3Bplus"]) else False, + usb4 = True, all = True, init = False, isRaspi2B = False, isRaspi3B = False, isRaspi3Bplus = False, + isRaspi4B = False, cpuRevision = 'unknown', piModel = 'unknown' ) def get_template_vars(self): return dict( - usb2 = self._settings.get(["usb2"]), - usb3 = self._settings.get(["usb3"]), usb4 = self._settings.get(["usb4"]), - usb5 = self._settings.get(["usb5"]), all = self._settings.get(["all"]), init = self._settings.get(["init"]), isRaspi2B = self._settings.get(["isRaspi2B"]), isRaspi3B = self._settings.get(["isRaspi3B"]), isRaspi3Bplus = self._settings.get(["isRaspi3Bplus"]), + isRaspi4B = self._settings.get(["isRaspi4B"]), cpuRevision = self._settings.get(["cpuRevision"]), piModel = self._settings.get(["piModel"]) ) @@ -73,49 +69,16 @@ def on_api_command(self, command, data): s.setBoolean(["plugins", "usbcontrol", "isRaspi2B"], True if piModel == "Raspi2B" else False) s.setBoolean(["plugins", "usbcontrol", "isRaspi3B"], True if piModel == "Raspi3B" else False) s.setBoolean(["plugins", "usbcontrol", "isRaspi3Bplus"], True if piModel == "Raspi3B+" else False) + s.setBoolean(["plugins", "usbcontrol", "isRaspi4B"], True if piModel == "Raspi4B" else False) s.set(["plugins", "usbcontrol", "cpuRevision"], cpuRevision) s.set(["plugins", "usbcontrol", "piModel"], piModel) s.save() - if command == "usb2": - strArg2 = "{arg2}".format(**data) - try: - self._logger.info("usb2 `{}`...".format(strArg2)) - location = "--loc=1-1.1" if self.get_template_vars()['isRaspi3Bplus'] else "--loc=1-1" - output = call(["sudo", "./uhubctl", location, "--ports=2", "--action=" + strArg2], cwd=uhubctlFolder) - if output > 0: - self._logger.info(" uhubctrl returned: {}".format(output)) - except OSError as e: - self._logger.info("uhubctl failed, throwing error") - output = "N/A" - if command == "usb3": - strArg3 = "{arg3}".format(**data) - try: - self._logger.info("usb3 `{}`...".format(strArg3)) - location = "--loc=1-1.1" if self.get_template_vars()['isRaspi3Bplus'] else "--loc=1-1" - self._logger.info("usb3 `{}`...".format(location)) - output = call(["sudo", "./uhubctl", location, "--ports=3", "--action=" + strArg3], cwd=uhubctlFolder) - if output > 0: - self._logger.info(" uhubctrl returned: {}".format(output)) - except OSError as e: - self._logger.info("uhubctl failed, throwing error") - output = "N/A" if command == "usb4": strArg4 = "{arg4}".format(**data) try: self._logger.info("usb4 `{}`...".format(strArg4)) - location = "--loc=1-1.1" if self.get_template_vars()['isRaspi3Bplus'] else "--loc=1-1" - output = call(["sudo", "./uhubctl", location, "--ports=4", "--action=" + strArg4], cwd=uhubctlFolder) - if output > 0: - self._logger.info(" uhubctrl returned: {}".format(output)) - except OSError as e: - self._logger.info("uhubctl failed, throwing error") - output = "N/A" - if command == "usb5": - strArg5 = "{arg5}".format(**data) - try: - self._logger.info("usb5 `{}`...".format(strArg5)) - location = "--loc=1-1.1" if self.get_template_vars()['isRaspi3Bplus'] else "--loc=1-1" - output = call(["sudo", "./uhubctl", location, "--ports=5", "--action=" + strArg5], cwd=uhubctlFolder) + location = "--loc=1-1" + output = call(["sudo", "./uhubctl", location, "--ports=1-4", "--action=" + strArg4], cwd=uhubctlFolder) if output > 0: self._logger.info(" uhubctrl returned: {}".format(output)) except OSError as e: @@ -157,6 +120,11 @@ def switch(self, key, default): 'a02082': 'Raspi3B', 'a22082': 'Raspi3B', 'a020d3': 'Raspi3B+', + 'a03111': 'Raspi4B', + 'b03111': 'Raspi4B', + 'b03112': 'Raspi4B', + 'c03111': 'Raspi4B', + 'c03112': 'Raspi4B', '9000c1': 'ZeroW', '9000C1': 'ZeroW', '900092': 'Zero', diff --git a/usbcontrol/bin/install b/usbcontrol/bin/install index 0e82c7a..3896547 100755 --- a/usbcontrol/bin/install +++ b/usbcontrol/bin/install @@ -7,6 +7,8 @@ if [ $ID -ne 0 ]; then echo "" else ln -s /home/pi/oprint/lib/python2.7/site-packages/usbcontrol/bin/uhubctl /usr/local/bin/uhubctl + chown root /home/pi/oprint/lib/python2.7/site-packages/usbcontrol/bin/uhubctl + chmod +s /home/pi/oprint/lib/python2.7/site-packages/usbcontrol/bin/uhubctl echo "pi ALL=(ALL) NOPASSWD: /home/pi/oprint/lib/python2.7/site-packages/usbcontrol/bin/uhubctl" > /etc/sudoers.d/uhubctl echo "If that ran without problems, the plugin should now be ready. Don't forget to press the Save button" echo "in the OctoPrint -> Settings -> USBControl page when finished here. Once you've done this then restart" diff --git a/usbcontrol/bin/uhubctl b/usbcontrol/bin/uhubctl index 5a238c8..db5bb03 100755 Binary files a/usbcontrol/bin/uhubctl and b/usbcontrol/bin/uhubctl differ diff --git a/usbcontrol/templates/usbcontrol_settings.jinja2 b/usbcontrol/templates/usbcontrol_settings.jinja2 index 6ab8682..938c87c 100644 --- a/usbcontrol/templates/usbcontrol_settings.jinja2 +++ b/usbcontrol/templates/usbcontrol_settings.jinja2 @@ -8,7 +8,7 @@

{{ _('Setup required') }}

First things first, we need to permission the pi user so that it can run the uhubctl executable without providing a password.

-

To do this, you'll need to remote into the Raspberry Pi 3B and run the following command:

+

To do this, you'll need to remote into the Raspberry Pi and run the following command:

sudo ~/oprint/lib/python2.7/site-packages/usbcontrol/bin/install

-
+
See the README.md for how to set things up. - Blue in the slider below indicates that 5V power is ON for the indicated port, the default.

+ Blue in the slider below indicates that 5V power is ON (default).

-
-

Important Raspberry Pi 2B/3B notice!

-

Given the internal design of the versions before the 3B+, one can only control USB power - inside the Raspberry for either all network adapters (Port 1) or all four ganged USB - ports (Ports 2-5) at once.

+
+

Important notice!

+

Given the internal design of Raspberry Pis, all four ports must be turned off/on at once.

-
- -
+

-
-
- +

-
+
It looks like your OctoPrint is installed on something other than a Raspberry Pi 2B,
- 3B or 3B+ and therefore isn't compatible with this plugin.


+ 3B, 3B+, 4B and therefore isn't compatible with this plugin.

{{ _('Why this might be useful') }}

@@ -85,7 +67,7 @@

It turns out that you can programmatically turn off the 5V pin on a per-port basis. That's the approach that I'm taking here instead of using tape.

-

This plugin uses uhubctl to turn USB power off and on. In addition to Raspberry Pi 2B, 3B and 3B+ it supports other USB hubs - refer +

This plugin uses uhubctl to turn USB power off and on. In addition to Raspberry Pi 2B, 3B, 3B+, and 4B it supports other USB hubs - refer to the list of supported devices on the uhubctl home page.

@@ -97,4 +79,4 @@
-
\ No newline at end of file +