Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

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.