Starting my video projector with Python and Playwright

I can’t exactly remember anymore what led me to the discovery, but last weekend I found out that my almost decade old Epson EH-TW650 video projector has online connectivity. It’s a relatively cheap model so during all these years I never even imagined it could have such thing.
It led me down a rabbit hole of discovering that there’s a web UI I can use to control it. And by adjusting some settings, I can have it listen to those commands when it’s off (or technically in stand-by mode) which means I can start it from my browser.
This led me to an adventure of making it easier and easier step-by-step for me to start it without having to find the remote.
First, I found its IP address through the on-screen menu and mapped it to
epson.local
in
/etc/hosts
because I would never
remember the IP address.
The web UI uses basic authentication and I got bored at going through it every time I opened it up so I figured: since it’s running on browser, I can automate things with Playwright.
edit: A reader reminded me that you can get through basic
auth by embedding the username and password into the URL as
https://username:password@www.example.com
. I knew that somewhere
in the back of my head but it's been so long since I've been dealing with
basic auth that I had completely forgotten it.
So I built a Python script that can be run with
uv
which takes care of installing
dependencies and running it in self-contained virtual environment. The shebang
at the start tells the script it should be run as a self-contained script with
uv
so I can save it as
/usr/local/bin/,epson
(see
Pesky little scripts
if you wonder why it starts with a comma) and run as
,epson
without having to invoke
uv
manually.
#!/usr/bin/env -S uv run --quiet --script
# requires installation of Playwright browsers before this script is run
# can be done with `pipx run playwright install`
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "python-dotenv",
# "playwright",
# ]
# ///
from playwright.sync_api import sync_playwright
from dotenv import dotenv_values
import os
creds = dotenv_values(f"{os.environ.get('HOME')}/.config/.epson-creds")
assert creds.get("EPSON_USERNAME") is not None, (
"You must define EPSON_USERNAME in $HOME/.config/.epson-creds"
)
assert creds.get("EPSON_PASSWORD") is not None, (
"You must define EPSON_PASSWORD in $HOME/.config/.epson-creds"
)
with sync_playwright() as p:
browser = p.firefox.launch()
# The projector's web UI uses basic auth
context = browser.new_context(
http_credentials={
"username": creds.get("EPSON_USERNAME"),
"password": creds.get("EPSON_PASSWORD"),
}
)
page = context.new_page()
# epson.local is my /etc/hosts alias for the actual projector IP
page.goto("http://epson.local/")
page.locator(".basic-control").click()
page.locator(".power").click()
browser.close()
First, it uses
PEP 723 inline script metadata
to describe the dependencies —
playwright
and
python-dotenv
in this case. Combined
with using uv
, it’s a real handy way to
define dependencies all within a single file without needing to set up a full
project and manual virtual environment.
I use dotenv
to read credentials from a
config. I then use playwright
to launch
an instance of Firefox, add the basic auth credentials, navigate to the local
Epson web UI, and click a couple of links and buttons.
After a short while, my projector peeps and powers on.
I’m planning to set up Home Assistant on a Raspberry Pi once I get a new one from a colleague and after that, I can move this functionality into a proper Home Assistant action and don’t need my Python script anymore.
Next steps
Between writing this code and writing this blog post, I bought a Raspberry Pi from a colleague and set up Home Assistant with automations that made this script useless for now. Now, whenever I boot up my Playstation 4, it will automatically power on my AV receiver and the projector.
Regardless, it was a fun exploration and it’s always great to build small automations via browser with Python and Playwright.
If something above resonated with you, let's start a discussion about it! Email me at juhamattisantala at gmail dot com and share your thoughts. In 2025, I want to have more deeper discussions with people from around the world and I'd love if you'd be part of that.