I like smart-home automation most when it removes technology from a process rather than adding more of it. A good automation should mean fewer apps, fewer menus and fewer things that somebody in the family needs to remember. Scanning documents turned out to be a surprisingly good example of this, because although our multifunction laser printer is perfectly capable of scanning documents to the network, the process of actually doing so was annoying enough that my wife simply didn’t want to use it.
The problem wasn’t that scanning was impossible or particularly difficult. It was the accumulation of small steps. You stand in front of a relatively small touchscreen, navigate into the scanning functions, select a destination, check the scan settings, choose what should happen with the file and finally start the scan. If something isn’t configured exactly as expected, there are a few more taps involved. None of that is terrible when you do it once, but for the very common task of taking a piece of paper and turning it into a PDF, it is far more interaction than should really be necessary.
There was another complication in our house: the printer normally has no power at all. It is connected through a smart plug and we only switch it on when we actually want to print or scan. This is convenient when printing because our offices are on different floors and I also occasionally work from the living room. If one of us wants to print something without being anywhere near the printer, we can simply switch it on through Home Assistant, an app or Alexa and send the print job.
For scanning, however, that workflow feels backwards. If I’m already standing in front of the printer holding a document, I don’t particularly want to take my phone out, find the printer switch, turn it on, wait for it to boot and then start navigating through another set of menus. Asking Alexa works too, but it still means that “scan this piece of paper” begins with a completely separate process just to wake up the machine.
My wife’s position on all of this was refreshingly straightforward: the whole process was too complicated, so scanning became something I tended to do. That was really the trigger for this project. Instead of teaching everybody how to operate the scanner, I wanted to make operating the scanner almost unnecessary.
The goal: paper in, button pressed, done
The interface I wanted was about as simple as it could possibly be. Put the document into the automatic document feeder, press one physical button and walk away. Depending on which button is pressed, the resulting PDF should either go into our document-management system or be sent to a specific email address. Home Assistant should take care of everything else, including waking the printer if necessary.
Once I started thinking about the workflow that way, Home Assistant was an obvious orchestration layer. It already controls the smart plug, receives events from Zigbee remotes, can send email through SMTP and can talk to OpenAI. The one missing piece was the ability to tell the multifunction printer itself to start scanning.
Finding a way to start a scan from Home Assistant
Home Assistant already has several integrations that can discover printers and expose things such as toner levels and printer status, but monitoring a printer and actively telling its scanner to create a document are two very different things. What I needed was support for eSCL, also commonly referred to in connection with AirScan.
eSCL is a network scanning protocol supported by many modern multifunction printers. Instead of installing a traditional scanner driver, a client can query the scanner for its capabilities, create a scan job and retrieve the generated pages over the network. That is exactly the abstraction I wanted: Home Assistant should become the scanner client, while the printer remains just a network appliance.
I found the HACS custom integration eSCL Scan for Home Assistant by wleonhardt. It communicates directly with eSCL scanners without requiring SANE, CUPS or another driver layer. Internally it queries ScannerStatus, creates jobs through ScanJobs and retrieves the resulting pages. It can also determine whether the automatic document feeder or the flatbed scanner should be used and exposes the current scan job through Home Assistant.
Installing the eSCL integration through HACS
The integration isn’t currently part of Home Assistant Core, so I installed it as a custom HACS repository. In HACS, open Integrations, use the menu in the upper-right corner and select Custom repositories. Add the GitHub repository wleonhardt/ha-escl-scan and choose Integration as the repository type. After that, install eSCL Scan from HACS and restart Home Assistant.
Once Home Assistant is back online, go to Settings → Devices & Services → Add Integration and search for eSCL Scan. The setup is done through Home Assistant’s normal configuration flow, so there is no YAML configuration required for the integration itself.
Connecting the scanner
The configuration dialog asks for the scanner’s hostname or IP address, network port, whether TLS should be used and, if required by the scanner, authentication credentials. There are also options for certificate verification and legacy TLS cipher suites, which can be useful with some older laser printers.
In my case the printer worked reliably over normal HTTP on port 80, so an anonymised version of my configuration looks roughly like this:
Scanner address: 192.168.x.x
Port: 80
Use TLS: No
Username: none
Password: none
Default DPI: 300
Default colour: Colour
File lifetime: 3600 seconds
The integration performs an eSCL ScannerStatus request while it is being configured, so a successful setup is already a useful compatibility test. Once connected, it creates a scan-status sensor in Home Assistant and can report states such as pending, processing and completed, along with information about the number of pages and whether the feeder or flatbed was used.
The integration also includes an optional Lovelace card. If you just want a convenient “Scan now” control in a Home Assistant dashboard, the card can be added with only a few lines:
type: custom:escl-scan-card
title: Scan nowThat already turns Home Assistant into a useful scanner interface, but my goal was different. I didn’t want to open a Home Assistant dashboard at all; I wanted a physical button sitting next to the printer.
Giving the scanner an automation-friendly action
The standard eSCL integration exposes its scan functionality through its dashboard card, authenticated HTTP endpoints and Home Assistant events. For my setup I added a small local wrapper around that functionality which exposes a Home Assistant action called escl_scan.scan_to_path. This isn’t part of the stock HACS integration, so if you’re reproducing the project exactly you would either need a similar wrapper or use the integration’s authenticated REST API and completion events instead.
The wrapper simply lets an automation specify the scanner connection, resolution, colour mode, destination directory and filename, while the eSCL integration continues to handle the actual communication with the device. An anonymised scan action looks like this:
- variables:
scan_filename: >
{{ now().strftime('%Y%m%d-%H%M%S') }}-scan.pdf
- action: escl_scan.scan_to_path
data:
entry_id: "YOUR_ESCL_CONFIG_ENTRY"
dpi: 300
color: color
destination: "/media/scans/outgoing"
filename: "{{ scan_filename }}"
timeout: 600The scanner automatically uses the document feeder when paper is present and falls back to the flatbed when it isn’t. That detail turned out to be useful because it means the physical interface doesn’t need another button for “ADF” versus “glass”; the scanner can simply make that decision itself.
First problem solved: the printer might be switched off
Because the printer is connected to a smart plug, every scan automation begins by checking its power state. If the printer is already on, the scan starts immediately. If it is off, Home Assistant switches the smart plug on and waits one minute for the printer to boot before sending the scan request.
This sounds like a small thing, but it removes an entire separate interaction from the process. The person standing at the scanner no longer needs to know or care whether the printer currently has power.
- if:
- condition: state
entity_id: switch.printer_power
state: "off"
then:
- action: switch.turn_on
target:
entity_id: switch.printer_power
- delay:
minutes: 1If the device is already running, that entire branch is skipped and there is no artificial one-minute delay. The difference between “printer on” and “printer off” effectively disappears from the user experience.
Physical buttons instead of touchscreen menus
For the physical interface I use inexpensive Zigbee buttons. Home Assistant receives each button press as an event and associates it with a destination. One button can mean “scan to my private email”, another “scan to my work email”, while buttons for my wife point to her respective destinations. A separate remote button sends documents straight into Paperless-ngx.
The actual trigger is intentionally boring. It is just an event generated by the remote:
triggers:
- trigger: event.received
target:
entity_id: event.scanner_button_1
options:
event_type:
- short_releaseThat simplicity is exactly the point. The physical button doesn’t understand scanning, email or AI. It simply tells Home Assistant which workflow the user wants, and Home Assistant takes it from there.
Scanning directly into Paperless
One of the destinations is the consume directory of our Paperless-ngx installation, which Home Assistant sees as network storage. For those scans I use a date-based filename with a daily counter, so documents arrive as files such as 20260910-scan-1.pdf, 20260910-scan-2.pdf and so on. The next day the counter starts again at one.
Paperless then takes over the job it is good at: OCR, indexing, document classification and long-term storage. I deliberately keep this workflow simple because Paperless is already the document-management system and doesn’t need Home Assistant to second-guess it.
Email scans are where AI becomes useful
The email workflow initially worked in much the same way. Press a button, create a timestamped PDF and send it through Home Assistant’s SMTP integration. That already saved a lot of time, but it produced emails and attachments with technically useful rather than human-friendly names. A filename such as 20260910-121423-scan.pdf is unique, but it tells me absolutely nothing about what I scanned.
At that point I realised that the PDF was already available inside Home Assistant and I already had an OpenAI AI Task provider configured. Instead of making somebody rename the document afterwards, I could let the automation inspect the content before sending the email.
Letting OpenAI classify the document
After the eSCL scan has finished, the PDF is passed to an OpenAI-backed Home Assistant AI Task. I don’t ask the model to produce a lengthy summary; the task is intentionally constrained to generating a short document description suitable for a filename. The instruction asks for the document type first, followed by the sender, company or topic where it can be identified reliably.
- action: ai_task.generate_data
data:
task_name: "Name scanned document"
entity_id: ai_task.my_openai_task
instructions: >
Analyse the attached scanned PDF and create a short,
clear document name.
Start with the document type and, when obvious,
add the sender, company or subject.
Examples:
"Invoice Telecom Provider"
"Receipt Pharmacy"
"Letter Insurance Company"
"Contract Mobile Provider"
Do not include customer numbers, account numbers,
invoice numbers or monetary amounts.
If the content cannot be identified reliably,
return "Document".
structure:
document_name:
description: "Short human-readable document name"
required: true
selector:
text:
attachments:
- media_content_id: >
media-source://media_source/local/scans/outgoing/{{ scan_filename }}
media_content_type: application/pdf
response_variable: document_analysis
continue_on_error: trueI use structured output here rather than accepting arbitrary prose from the model. The automation expects one field called document_name, which makes the response predictable and easy to reuse in later steps. It also avoids the classic AI response where you ask for three words and get a polite introductory paragraph, an explanation and a conclusion for free.
Turning the result into a filename
The AI-generated name isn’t used blindly as a filesystem name. Before it is presented as an email attachment, the automation removes potentially problematic characters, replaces spaces with hyphens and keeps the date at the beginning. If the AI request fails completely, the scan is still sent under a generic fallback name rather than allowing an optional feature to break the entire workflow.
- variables:
document_name: >
{% if document_analysis is defined
and document_analysis.get('data', {}).get('document_name') %}
{{ document_analysis['data']['document_name'] }}
{% else %}
Document
{% endif %}
safe_document_name: >
{{ document_name
| regex_replace('[^0-9A-Za-zÄÖÜäöüß _.-]', '')
| regex_replace('\\s+', '-')
| regex_replace('-+', '-') }}
mail_filename: >
{{ now().strftime('%Y%m%d') }}-{{ safe_document_name }}.pdfA temporary file called 20260910-121423-scan.pdf might therefore appear in the recipient’s inbox as 20260910-Invoice-Telecom-Provider.pdf. A pharmacy receipt could become 20260910-Receipt-Pharmacy.pdf, while an insurance letter gets an equally obvious name. The temporary file on the server can keep its timestamped name, so even two documents of the same type scanned on the same day cannot overwrite each other.
The email subject is generated at the same time
The same description is also used to create the subject of the email. Instead of opening an inbox full of messages simply called “Scan”, the recipient sees something such as Scan from 10.09.2026: Invoice Telecom Provider. That makes the automation useful even before the attachment is opened and turns the inbox itself into a much more meaningful list of scanned documents.
- action: smtp.send_message
target:
entity_id: notify.scanner_recipient
data:
title: >
Scan from {{ now().strftime('%d.%m.%Y') }}: {{ document_name }}
message: "Attached is the scanned document."
attachments:
- media_source:
media_content_id: >
media-source://media_source/local/scans/outgoing/{{ scan_filename }}
media_content_type: application/pdf
filename: "{{ mail_filename }}"Home Assistant’s SMTP integration can have several recipient entities, so the actual scanning and analysis logic can stay the same while different physical buttons select different email destinations. In my installation that means separate buttons for private and work destinations for both of us, without any addresses being entered on the printer itself.
What the whole automation now does
A single button press now starts a surprisingly long chain of events. Home Assistant first checks whether the printer has power. If necessary, it switches on the smart plug and gives the machine a minute to boot. It then starts an eSCL scan, automatically choosing the feeder when a document is present, and writes the resulting PDF to network storage. For email workflows the PDF is subsequently sent to OpenAI, which determines a sensible description, and Home Assistant sanitises that result into a filename before finally sending the document through SMTP to the destination represented by the button that was pressed.
The person using the system sees none of this. There is no Home Assistant dashboard to open, no scanner destination to choose, no email address to type and no filename to invent. You put the paper in and press one button.
Why this saves much more time than it sounds like
If you look at a single scan, saving a minute or two might not sound revolutionary. The difference becomes much more noticeable when you consider the number of interactions being removed. Previously I might have had to wake the printer, wait for it, navigate its touchscreen, choose the correct scan function and destination, start the scan, find the resulting document and perhaps rename it afterwards. My wife often solved that entire chain of interactions by simply handing the document to me instead.
Now the mental model is the same for everybody in the house: insert document, press the button corresponding to where it should go, and leave. The automation handles not only the repetitive technical steps but also the tiny administrative job that came afterwards. Having AI look at the document and create a useful filename sounds like the flashy part of the project, but in practice it is simply another little piece of friction that has disappeared.
The best smart home is the one you don’t have to operate
This project is a good example of why I enjoy Home Assistant. None of the individual technologies is particularly exotic. A smart plug switches power, Zigbee gives me a physical button, eSCL talks to the scanner, SMTP sends mail, Paperless manages documents and OpenAI can understand the content of a PDF. The interesting part is connecting them in such a way that the complexity disappears from the person actually using the system.
I could have written instructions explaining how to use all of the scanner’s menus. Instead I replaced the instructions with four buttons. That is a much better user interface.
And the best confirmation came when my wife looked at the new setup, pointed at one of the buttons and essentially asked, “So I just put the paper in and press this one?”
Yes. Exactly.
Technical note: The code examples in this article are intentionally simplified and anonymized. Network addresses, entity IDs, storage paths, mail destinations and configuration-entry identifiers have been replaced with generic examples. The escl_scan.scan_to_path action shown here is a small local extension I added to my installation; the upstream eSCL Scan integration provides the scanner communication, status sensor, events, Lovelace card and authenticated REST API on which that wrapper is based.