hp 2840 scan-to-folder button under linux

Asked by saliya on 2008-06-05

Well, seems like this question's already been asked-and-answered.

Consider this another vote for 'implementation of scan-to-network' functionality :)
I'm using hplip 2.8.2 under Ubuntu, and I haven't tried to upgrade to 2.8.5 because
prior answers (2008-02-25, by Aaron Albright) imply the feature's not available.
If it _is_ available somewhere, well, just point me at it.

What I'm trying to achieve:

electronic archival of sent/received faxes and scanned documents.
Ideally this would be 'a sent/received fax automatically gets archived' and
'put a document into the document feeder, press the button, it gets archived'

along with the other functions that are already available; like print/fax from
Linux PC; scan from Linux PC; check printer status from Linux PC.

What steps I take:

Currently using xsane to manually scan documents individually.
Specifying number of pages to scan means I have to count them first.

What happens:

Xsane turns the document into a PDF that I can then save wherever I like.

What I think should happen:

I think that support of a commonly-available network sharing mechanism - e.g.
Samba, or FTP - should be available on the printer itself. A fallback position would
be to have the hplip implement the same things the Windows driver does.

Taking the PS input that would normally be fed to the printing engine and blatting
it back out the network port to a Samba share would be good enough for me I think...
the filename could be something like 'YYYYMMDD-HHMMSS-PrinterMAC-from_fax_number.ps'
or 'YYYYMMDD-HHMMSS-PrinterMAC-scan.ps' to provide the information (sender fax #)
that's lost upon conversion to an image format and hopefully prevent filename collisions.
Once we have the PS we can do anything :)

Automatic conversion to PDF or TIFF or something like that would be nicer.

Incidentally; under Ubuntu 8.04 apt-get install hplip worked perfectly for all
the supported functionality. This is how things _should_ be installed.
Kudos to you devs for where it's at today, once again we're buying HP product
because it's the only reasonably-priced kit that works well under Linux. Keep it up :)



Question information

English Edit question
HPLIP Edit question
No assignee Edit question
Solved by:
Last query:
Last reply:


Thanks for the note. This feature is still not supported as of yet. However I'll be sure to note your vote for this in the future.

Sorry for any inconvenience and thanks for your support of HPLIP!


saliya (saliya-ndm) said : #2


thanks for the reply; we'll file this away as a 'solved' :)

benoitlyon (bendrd) said : #3

i'm working on debian 5.0 amd64. I can not configure the button scan-to-folder on my hp2480.

if I understand correctly, you scanned from xsane from linux. but you can not use the button to scan.
Or did you found a solution to use this button scan-to-folder ?

thank you

saliya (saliya-ndm) said : #4

No, you cannot use the button to scan using default software/drivers on Linux.

I found a solution; http://rende.se/index.php?n=Main.ScanToFolder

which I then used as a basis to write my own daemon using Perl's WWW::Mechanize.

I guess if I'd written it in python I could have sent it back to the HPLIP devs :P

My daemon sets a single scan-to destination (this is just an identifying string).
Then it polls the printer every 5 sec to see whether the button's been pressed and
if so uses /usr/bin/hp-scan to pull the file in to the destination folder as a PNG.

If anyone's interested I'll GPL it (it's been working well for a few months now).



saliya (saliya-ndm) said : #5

#!/usr/bin/perl -w
# quick hack to
# a) setup a single default scan-to destination on the HP after reboot
# b) activate that destination when scan-to is pressed
# Only supports 1 simultaneous printer
# requires apt-get install libwww-mechanize-perl
# mkdir /var/log/scanner && chmod 777 /var/log/scanner
# installation of hp-scan utility from hplip
# Any suggestions for improvements (I'm sure there are lots)
# may be addressed to me care of launchpad article
# https://answers.launchpad.net/hplip/+question/35378
# Sometimes the hp-scan software locks up (has happened a couple of times over a couple of years).
# on these occasions you must stop/restart this software.
# Copyright Saliya Wimalaratne 2008-2010
# This file is free software;
# you can redistribute it and/or modify it under the terms of the GNU
# General Public License (GPL) version 2 as published by the Free Software Foundation.
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY of any kind.

use strict;
use WWW::Mechanize;
use POSIX qw(strftime);

my $cfg;

# ip of printer, will read as argument
$cfg->{'ip'} = shift(@ARGV) || '';

$cfg->{'logfile'} = "/var/log/scanner/hp_scanto_output.log";
Log("INFO: $0: Started, using IP $cfg->{'ip'}", $cfg->{'logfile'});

# Feel free to alter any parameters to suit yourself
# stuff hp-scan needs.
$cfg->{'hp_scan'} = "/usr/bin/hp-scan -n -mcolor -r150 --size=a4";
# the ID of the scanning host that will show on the printer
$cfg->{'hostid'} = "SERVER";
# the ID of the destination folder that will show on the printer
$cfg->{'devicedisplay'} = $cfg->{'hostid'} . ":DOCS";
# where the output files go (needs to be writable by the user running the prog)
$cfg->{'output_dir'} = "/space/unfiled";

# I wouldn't change these.
$cfg->{'mech'} = WWW::Mechanize->new(autocheck => 0);
$cfg->{'scan_destinations'}{'list'} = "http://" . $cfg->{'ip'} . "/hp/device/info_scanto_destinations.xml";
$cfg->{'scan_destinations'}{'modify'} = "http://" . $cfg->{'ip'} . "/hp/device/set_config.html";
$cfg->{'notifications'} = "http://" . $cfg->{'ip'} . "/hp/device/notifications.xml";

# the program
my $counter = 0;

while (1)
 # log every 10 mins
 if (($counter % 60) == 0)
  $counter = 0;
  Log("MARK: $0 running", $cfg->{'logfile'});

 if (&poll_for_button_press($cfg))
  while (&document_loaded($cfg))

Log("INFO: $0 finished", $cfg->{'logfile'});


sub document_loaded
 my $lfn = "document_loaded";
 my $cfg = shift;

 my $logstr = "$lfn: IP:$cfg->{'ip'}";
 my $logfile = $cfg->{'logfile'};
 my $uri = $cfg->{'notifications'};

 # ask the printer if there is a document ready
 my $response = $cfg->{'mech'}->get( $uri );
 if (!$response->is_success)
  Log(sprintf("ERROR: $logstr: Can't fetch $uri: '%s': returning", $response->status_line), $logfile);
  return 0;

 # print $response->content;
 if ($response->content =~ /<ADFLoaded>1<\/ADFLoaded>/)
  Log("INFO: $logstr: Document feeder is loaded: scheduling scan", $logfile);
  return 1;

 Log("WARNING: $logstr: Document feeder is NOT loaded: not scanning", $logfile);
 return 0;

sub scan_to_file
 my $lfn = "scan_to_file";

 my $cfg = shift;

 my $logstr = "$lfn: IP:$cfg->{'ip'}";
 my $logfile = $cfg->{'logfile'};

 # place to scan to?
 if (! -d $cfg->{'output_dir'})
  Log("WARNING: $lfn: Output directory '$cfg->{'output_dir'}' doesn't exist: attempting to create", $logfile);
  if (system("/bin/mkdir -p $cfg->{'output_dir'}") != 0)
   Log("ERROR: $lfn: Output directory '$cfg->{'output_dir'}' cannot be created: $!: exiting", $logfile);

 # output filename
 my $timestr = strftime("%Y%m%d-%H%M%S", localtime( time()));

 my $fn = sprintf("scanned_%s.png", $timestr);

 my $full_fn = $cfg->{'output_dir'} . "/" . $fn;
 my $tmpfn = $full_fn;
 my $count = 0;
 my $max_count = 20;

 if (-e $tmpfn)
  while(-e $tmpfn && ($count < $max_count))
   Log(sprintf("WARNING: $lfn: Destination file '%s' exists: trying another (%d/%d)", $tmpfn, $count, $max_count), $logfile);
   $tmpfn = sprintf("scanned_%s_%02d.png", $timestr, $count);
  $full_fn = $tmpfn;

 Log("INFO: $logstr: Scan-to-file requested: initiating scan to $full_fn", $logfile);

 my $syscmd = sprintf("%s --output=%s", $cfg->{'hp_scan'}, $full_fn);

 if (system($syscmd) != 0)
  Log("ERROR: $logstr: System command '$syscmd' failed: returning", $logfile);

 # success
 Log("INFO: $logstr: Scan-to-file successfully completed ($full_fn)", $logfile);


sub poll_for_button_press
 my $lfn = "poll_for_button_press";

 my $cfg = shift;

 my $logstr = "$lfn: IP:$cfg->{'ip'}";
 my $logfile = $cfg->{'logfile'};

 my $uri = $cfg->{'notifications'};

 # ask the printer if the button's been pressed
 my $response = $cfg->{'mech'}->get( $uri );
 if (!$response->is_success)
  Log(sprintf("ERROR: $logstr: Can't fetch $uri: '%s': returning", $response->status_line), $logfile);
  return 0;

 # print $response->content;
 if ($response->content =~ /<ScanToDeviceDisplay>$cfg->{'devicedisplay'}<\/ScanToDeviceDisplay>/)
  Log("INFO: $logstr: Network scan requested for $cfg->{'devicedisplay'}: scheduling scan", $logfile);
  return 1;

 return 0;


sub setup_destinations
 my $lfn = "setup_destinations";

 my $cfg = shift;

 my $logstr = "$lfn: IP:$cfg->{'ip'}";

 # list destinations
 my $uri = $cfg->{'scan_destinations'}{'list'};
 my $logfile = $cfg->{'logfile'};

 my $response = $cfg->{'mech'}->get( $uri );

 if (!$response->is_success)
  Log(sprintf("ERROR: $logstr: Can't fetch $uri: '%s': returning", $response->status_line), $logfile);

 # print $response->content;

 if ($response->content =~ /<DeviceDisplay>$cfg->{'devicedisplay'}<\/DeviceDisplay>/)
  # Log("DEBUG: $logstr: Destination '$cfg->{'devicedisplay'}' exists: returning", $logfile);

 Log("INFO: $logstr: Destination '$cfg->{'devicedisplay'}' doesn't exist: adding", $logfile);

 # destination doesn't exist, set it up
 $uri = $cfg->{'scan_destinations'}{'modify'};
 $response = $cfg->{'mech'}->post($uri, [ 'AddScanToDest_1' => sprintf("^%s^DestFolder", $cfg->{'hostid'}, $cfg->{'devicedisplay'}) ]);

 if (!$response->is_success)
  Log(sprintf("ERROR: $logstr: Can't POST to $uri: '%s': returning", $response->status_line), $logfile);

 Log("INFO: $logstr: Added destination $cfg->{'devicedisplay'}", $logfile);

sub Log

     my $logstring = $_[0];
     my $logfile = $_[1] || "/var/log/default-perl-logfile.log";

     # logfile is "logfile-YYYY-MM";
     if ($logfile !~ /-(\d{4})-(\d{2})$/)
      # logfile is not in correct format, modify

  # extract all digits from logfile
  $logfile =~ tr/[0-9]//d;

  # delete last non-word characters
  $logfile =~ s/(\W+)$//g;

  # append -YYYY-MM
  $logfile .= strftime("-%Y-%m", localtime( time() ) );



 open(LOG, ">>$logfile") or die "ERROR: Can't open logfile $logfile: $!\n";
 select(LOG); $|=1; select(STDOUT);

 my $logdate = strftime("%Y-%m-%d %H:%M:%S", localtime( time() ) );

 print LOG "$logdate $logstring\n" or die "ERROR:PerlLog: Cannot write to logfile $logfile: $!\n";


Hetz Ben Hamo (hetz) said : #6

Hi Saliya, others..
Great script, but my question is: is there a way to use it on a printer which is connected through USB? My OfficeJet J5783 doesn't have Ethernet connection nor a WiFi connection.

saliya (saliya-ndm) said : #7

Hi Ben,

Given that the printer it was written for is a 2840 network-connected printer (and it uses network functionality that is probably specific to that printer) I would guess that the answer is "no".

Your printer may well support similar functionality via the USB interface but I doubt the above program will get you there; you want to start your own Launchpad thread on your printer.



junkzilla (junkzilla-bugmenot) said : #8

The hidden link urls don't seem to work anymore. I'm using the officejet 4500 AIO, does anyone know what the new URLs look like ?

junkzilla (junkzilla-bugmenot) said : #9

Ok, so I sniffed the packets and noticed that they are not using http port anymore. There's some proprietary encoding but I do see the plain text with the scan-to options being set. I unfortunately don't have the time to reverse engineer this (what looks like a simple) protocol.
HP: please document the protocol for us Linux people.

stillife (stillife) said : #10

Hi Saliya,

Thanks for posting your script. I've got it setup and running. However, something goes wrong when I start my scan. I get the following errors:

Traceback (most recent call last):
  File "/usr/bin/hp-scan", line 213, in <module>
  File "/usr/share/hplip/base/module.py", line 390, in parseStdOpts
    self.usage(show_usage, error_msg)
  File "/usr/share/hplip/base/module.py", line 422, in usage
    utils.format_text(self.usage_data, show_usage, self.title, self.mod, self.version)
  File "/usr/share/hplip/base/utils.py", line 1372, in format_text
    log.info(formatter.compose((text1, text2), trailing_space))
  File "/usr/share/hplip/base/utils.py", line 221, in compose
  File "/usr/share/hplip/base/utils.py", line 255, in wrap
    for i in range(0, len(word), self.width):
ValueError: range() step argument must not be zero

Can you guide me?