Automatically powering your printer down with OpenHAB

Introduction to OpenHAB

OpenHAB is an open source, Java-powered Home Automation Bus; what this basically means is it’s a centralized hub that can connect to many systems that have found their way into your house, such as your smart TV, your ethernet-capable Audio Receiver, Z-Wave- or Zigbee-powered accessories, HVAC systems, Kodi (aka XBMC), CUPS, and even anything that speaks SNMP or MQTT. Other ways of getting data into OpenHAB (such as an HTTP REST interface, both ways) mean it’s pretty much fully able to integrate into anything you can throw at it.

Installation and configuration is not for the faint of heart, however, and at least in v1.x setup takes a bit of getting used to, but once you get to grips with the configuration, items, rulesets and sitemaps you have the power of your entire house at your fingertips.

Premise

Since I’ve gotten into home automation, I’ve started measuring many devices in my house (continuously) to see how much power they consume. One of the silent, hidden away, power guzzlers is my laser printer. I don’t print often, but I do have a nice color laser printer at my disposal. It used to be powered on continuously, but apparently that meant I was giving away a whopping 20 Watts, day in, day out, with absolutely nothing to show for it.

Now, I’m definitely too lazy to go and turn on the printer every time I want something printed – moreover, it’s a network printer and we also print from our laptops on another floor; not nice to have to go to the top floor only to see you still need to power on the printer, wait for it to warm up and then wait for it to actually print. So, with a little investment in hardware and technology, there is of course a geeky solution to all of this.

I print through a CUPS server on my local fileserver – OpenHAB nicely integrates with CUPS. The idea is that the printer stays powered off until OpenHAB detects an entry in the CUPS print queue for that particular printer. Once that is detected, the printer is powered up. Once the print job has completed, the printer can be powered off again – with a small timeout in case more jobs follow, so we don’t needlessly toggle power to the printer. Do note that the printer is mercilessly powered off by this solution, as if you would unplug the cord from the wall. If your printer is not comfortable with this type of shutdown, I don’t advise to try and kill it this way.

It does not matter what you technology use to control power to your printer, a Z-Wave plug, an RF-controlled power socket, or even an APC MasterSwitch via SNMP – any of the technologies OpenHAB supports is fine.

OpenHAB example script

I was inspired by this CUPS binding example given in the OpenHAB wiki (see “Example Use Case”), but it’s not complete.

My printer has a fairly large internal memory, so it is possible that the print job is fully flushed from CUPS, but still in the printer RAM, waiting to be printed. It’s even possible that multiple print jobs wait in the printer’s memory instead of the CUPS server. The example script only waits 5 minutes after CUPS sees no more jobs, and then powers off the printer – it is possible the print job(s) were not finished, even if CUPS thought they were. Further more, if an out of paper issue (or paper jam, or …) occurs, the script would still just blindly power off.

I’ve combined my SNMP knowledge with this example script, and after a few iterations of polishing and finding new edge cases, I’ve now got a script that will shut down the printer 5 minutes after CUPS has no more jobs in the print queue AND the printer indicates it is idle.

OpenHAB configuration

Resummarizing the requirements:

  • You print through a CUPS server to your printer
  • You have a way of remotely shutting power to your printer
  • Your printer supports SNMP, specifically hrPrinterStatus from HOST-RESOURCES-MIB
  • Your printer has a static IP (or at least is assigned a consistent IP via DHCP)
  • Your printer does not mind its power being yanked out from under its feet

First, we need to copy OpenHAB’s SNMP and CUPS bindings into the addons/ folder in the OpenHAB root: org.openhab.binding.snmp-1.7.x.jar and org.openhab.binding.cups-1.7.x.jar. To load new addons (unlike new rules, items etc), OpenHAB needs to be restarted, but hold on a little – we need to adjust openhab.cfg and as far as I know, adjusting this one requires a restart as well.

Next, we make sure we configure our CUPS server in OpenHAB’s configuration/openhab.cfg – I’ve set the refresh frequency to 30 seconds instead of 60. This speeds up detection of a job when the printer is off.

# CupsServer IP address or Host name
cups:host=cups-server-hostname.example.com

# CupsServer Port (optional, defaults to 631)
cups:port=631

# refresh interval in milliseconds (optional, defaults to 60000)
cups:refresh=30000

Then we create the items for this printer, configuration/items/printer.items – one linking to the CUPS queue, one which instructs OpenHAB do poll the hrPrinterStatus OID every 30 seconds, and one that is pointing to my Z-Wave power module at node 4:

Number Printer_Desk_Jobs_Queued         "Office Printer Job Queue" (SF_Office) { cups="Desk#NOT_COMPLETED" }

// HOST-RESOURCES-MIB::hrPrinterStatus.1 = INTEGER: idle(3)
Number Printer_Desk_Status              "Office Printer Status [%s]"       (SF_Office) { snmp="<[192.168.0.99:public:.1.3.6.1.2.1.25.3.5.1.1.1:30000]" }

Switch Printer_Desk_Switch              "Office Printer" (SF_Office) {zwave="4:command=switch_binary"}

Finally, the rules file which has the code to make the above a reality, configuration/rules/printer.rules:

import org.openhab.model.script.actions.Timer
var Timer printerDeskTimer = null

rule "CUPS-Printer"
when
  Item Printer_Desk_Jobs_Queued changed or
  Item Printer_Desk_Status changed
then

  // Check if we have any queued jobs.
  if (Printer_Desk_Jobs_Queued.state != 0) {
    // Yes, we have queued jobs.
    logInfo("CUPS-Printer", Printer_Desk_Jobs_Queued.state+" queued job(s) found")

    // Cancel running timer if any
    if (printerDeskTimer!=null) {
      logInfo("CUPS-Printer", "Cancelling running shutdown timer")
      printerDeskTimer.cancel
      printerDeskTimer=null
    }

    // Turn on printer if it's off.
    if (Printer_Desk_Switch.state!=ON) {
      logInfo("CUPS-Printer", "Powering on printer")
      sendCommand(Printer_Desk_Switch,ON)
    } else {
      logInfo("CUPS-Printer", "Printer is already on")
    }
  } else {
    // No queued jobs.
    logInfo("CUPS-Printer", "No more jobs queued")

    // Printer status idle?
    if (Printer_Desk_Status.state==3) {
      logInfo("CUPS-Printer", "Printer is idle, shutting down printer in 5 minutes")
      // Turn off printer in 5 minutes
      // If it starts printing again, timer is killed.
      printerDeskTimer = createTimer(now.plusMinutes(5)) [|
        sendCommand(Printer_Desk_Switch,OFF)
      ]
    } else {
      logInfo("CUPS-Printer", "Printer is not idle ("+Printer_Desk_Status.state+"), waiting for status 3")

      // Cancel running timer if any - possibly a job came through cups in between our refresh time without OpenHAB noticing
      if (printerDeskTimer!=null) {
        logInfo("CUPS-Printer", "Cancelling running shutdown timer")
        printerDeskTimer.cancel
        printerDeskTimer=null
      }
    }
  }
end

I haven’t run into any issues running this in the background, I have a power-conscious print server now, without having to put manual effort into powering the printer on! If you run into issues, please leave a comment!

Writing informative technical how-to documentation takes time, dedication and knowledge. Should my blog series have helped you in getting things working the way you want them to, or configure certain software step by step, feel free to tip me via PayPal (paypal@powersource.cx) or the Flattr button. Thanks!