koldfront

Turning the printer on and off automatically #automation

🕥︎ - 2021-04-29

The second idea I had for the usb relays I bought was to turn my printer on and off automatically, as is is located in a place where access to the power button means getting on the floor and twisting underneath a desk. Also the has printer lights that are annoying, even when it goes into "hibernation" mode, so I want it turned off when not in use.

I have an access point which has a usb port, and since the access point is always on and it is in the vicinity of the printer, it's a good candidate for controlling the relay.

I connected the usb-cable to the access point and installed the crelay package, which supports my usb controlled relay, and then I connected the power cable to the printer. The access point runs OpenWrt so packages like this are readily available.

So now I can turn the printer on by ssh'ing to the access point and running the command crelay 1 ON. Turning it off is similarly simple. I copied the contents of /root/.ssh/id_rsa.pub on my computer to /etc/dropbear/authorized_keys on the access point to allow non-interactive ssh access.

On my computer, I'm using CUPS for printing; after a bit of searching I found and installed the Tea4CUPS package, and added these two lines to /etc/cups/tea4cups.conf:

prehook_power : ssh accesspoint.koldfront.dk crelay 1 ON
posthook_power : echo /usr/local/bin/printer_off "$TEAPRINTERNAME" | at now + 5 minutes

and preprended tea4cups: to the DeviceURI in /etc/cups/printers.conf.

The prehook runs when a new print jobs enters the queue, and turns the printer on (if the printer is already on, there is no change).

The posthook runs every time a print job finishes, so what I have it do is to run a little script 5 minutes later. The script checks if the print queue is empty and if it is, it turns the printer off. If it isn't, it uses at(1) to run itself after 5 more minutes, and so on, until the print queue finally is empty. This is the script:

#!/bin/sh

# printer_off - Turn printer off if queue is empty. If not, try again
#               in 5 minutes.

if lpq -P "$1" | grep -q 'no entries'; then
    ssh accesspoint.koldfront.dk crelay 1 OFF
else
    echo "$*" | at now + 5 minues
fi

What I like about this hook solution is that the printer is turned on when a job is created, and there is no polling/busy looping to detect jobs appearing in the print queue.

Recently an article on this subject caught my eye: "Home Assistant Printer Power Management". It might be interesting to contrast the two solutions.

What I didn't do: run things in docker containers, run a message queue, run a script that calls lpq every minute. Not to mention setting up HomeAssistant.

My script is 10 lines + 3 config lines for the hooks, compared to 104 lines of script + 25 lines of YAML.

If you are already running HomeAssistant, it might be nicer to integrate the printer into it. But also a lot more complicated, it seems.

Thanks for your blog post. The at command in the posthook is important. I spawned a background child job, but the posthook was active until the child process ended, which was not what I wanted. With the at it works well.

I am wondering though if your solution has a race condition:

Assume you have the printer_off script run by the posthook finding out that the print queue is empty.

Not a new job could come in and the prehook for the new job powers on the printer.

Now the printer power off in the posthook gets executed (the print queue was checked before the new job came in).

rd@home:~$ cat /usr/local/bin/printjob_add.sh 
#!/bin/bash

# update active job count and update printer power state

# $1 Filename
# $2 adder


if [ -e $1 ]; then
    COUNTER=$(tail -1 $1);
else
    COUNTER=0;
fi;
date --rfc-3339=ns > $1
COUNTER=$((COUNTER + $2))
echo $COUNTER >> $1


if [ $COUNTER -eq 0 ]; then
    knxtool groupswrite ip:127.0.0.1 2/6/0 0 > /dev/null 2> /dev/null
    sleep 1
else
    knxtool groupswrite ip:127.0.0.1 2/6/0 1 > /dev/null 2> /dev/null
    sleep 1
fi
rd@home:~$ 

and then called this script with a file lock in the prehook:

flock -x $LASTPRINTFILE /usr/local/bin/printjob_add.sh $LASTPRINTFILE 1

and the posthook:

echo flock -x $LASTPRINTFILE /usr/local/bin/printjob_add.sh $LASTPRINTFILE -1 | at now + 10 minutes

(using your nice at solution :-))

Thanks for your post again Rainer

- Rainer Dorsch 🕤︎ - 2021-10-20

+=

Rainer Dorsch writes:

I am wondering though if your solution has a race condition:

You're absolutely right, it does.

In this case I prefer to skip the complexity associated with trying to mitigate the tiny race condition.

Some other computer could also try to use the printer, which wouldn't even make the printer turn on - so this solution isn't comprehensive, but for my use it is sufficient.

Thanks for your comment, I'm happy my post was useful!

- Adam Sjøgren 🕙︎ - 2021-10-20

+=

Add comment

To avoid spam many websites make you fill out a CAPTCHA, or log in via an account at a corporation such as Twitter, Facebook, Google or even Microsoft GitHub.

I have chosen to use a more old school method of spam prevention.

To post a comment here, you need to:

¹ Such as Thunderbird, Pan, slrn, tin or Gnus (part of Emacs).

Or, you can fill in this form:

+=