About a month ago, I found a decent deal on a refurbished BenQ projector and decided it was finally time to put something more interesting than a blank wall in front of my couch. I’d naively hoped the projector would support HDMI-CEC and allow my Chromecast (and thus my nearby smart speaker) to control its power state and input. Unfortunately, as with most projectors, this 1-year-old projector still doesn’t support the now 16-year-old protocol, and so I resigned myself to use its remote like a regular human.

Naturally, with literal seconds of effort per day wasted manually pressing the power button, it only took me a week to decide that I’d need to build my own solution.

Part 1: Research

Remotely controlling AV devices is ostensibly a solved problem, at least if they support IR remotes. An IR blaster supported by LIRC is often enough to manage devices, however IR can be unreliable and, importantly, leaves you to guess at the device’s current state which might change if someone ever presses a button without going through your app. While it’d certainly be possible to build a solution this way, I was really looking for the 100% solution rather than the 80%.

However, looking over the projector’s available IO ports, I noticed an RS-232 port. Initially I’d assumed it was some sort of factory interface but after a few quick searches it turned out to be a surprisingly well documented serial interface to nearly every setting that can be configured on the projector. This interface seems to be supported by most BenQ-branded projectors though the particular commands supported may differ. Make sure to check your projector’s manual for specifics - it’ll be there if its supported.

[insert back photo here]

Testing the Serial Interface

Actually issuing commands to the projector is pretty straightforward and can be tested by hand with screen:

# below examples assume the serial device is /dev/ttyUSB0

# convert outgoing \n to \r (per the projector's serial protocol)
stty -F onlcr /dev/ttyUSB0

# open the serial port
screen /dev/ttyUSB0 115200

# next, press enter a few times until you see a >, then type:
*pow=?#

# ... and press enter again to see the response

The projector should reply with either *POW=OFF# or *POW=ON#, depending on its current state, but may also return block item if the command or state is invalid, e.g. currently in the process of turning on or off.

Quit screen by pressing Ctrl+a, typing :quit, and pressing enter.

Smart Home Integration

Even with a solid plan for interacting with the projector, I’d still need a way to actually control it. My first thought was to look at integrating with Google Actions directly - this would provide voice and app controls, and with any luck, the Chromecast would notice the controllable TV device in the Home Graph and defer to it for power controls.

It didn’t take long to realize that this wouldn’t be worth the effort: Google’s Smart Home actions require a full OAuth2 identity stack with sane auth flows before you can even think about building your actual functionality. After fighting with Actions for a few hours simply trying to get auth working, I realized this was all beginning to feel like actual work work, and decided to look for other options.

Luckily, I remembered that Home Assistant can integrate with just about every smart home platform, Google Home included, and it seemed relatively straightforward to create a custom device integration.

Part 2: The Implementation

To start, we’ll need a few pieces of hardware:

  • A machine to run the projector controller daemon. I used a spare Raspberry Pi 3b+, but any computer ought to work, though I’ve only tested my this on Linux.

    This could potentially run alongside the Home Assistant install, but both Docker and the Home Assistant Operating System make doing so complicated.

  • An RS232 adapter and a null modem cable to communicate with the projector

    My particular projector didn’t require a crossover cable, but some may - make sure to check your projector’s manual if you do this yourself.

  • A machine running Home Assistant, such as a Raspberry Pi running the Home Assistant Operating System, or a server running it in Docker. Either way, it needs to have working mDNS service discovery (so --net host in Docker). I’m running the Home Assistant Docker image on a small Intel-based LarkBox server.

    This is optional if you just want to use the REST API.

TODO

Part 3: Home Assistant Integration

TODO

Conclusion and Next Steps

I’d say this got me about 90% of what I wanted. There’s still some caveats:

  • My Google Home devices frequently complain about commands failing when they actually succeed. I think this might be related to the power on / off requests taking a long time to return (potentially 60 seconds).
  • The Chromecast still doesn’t automatically power on the projector when I ask it to play something, and apparently it isn’t quite smart enough to query for other devices that might provide control functions. Implementing properly would require actual HDMI-CEC.
  • I run my input devices through an HDMI switch to extract high quality audio rather than using the projector’s noisy 3.5mm jack, so I don’t benefit from the existing input switching. I’d like to build IR control for this.

In part two, we’ll try to transparently implement HDMI-CEC for the projector itself.