JVC Projector Plugin (HFN)

This is a plugin for JVC Projectors that are controlled either directly over IP or over a serial connection by a Global Cache IP2SL.

While I was waiting to receive my new projector, @LJR had already gotten his and I offered some assistance as he put together another JVC Projector Plugin. Once my projector finally arrived, I installed that plugin and was able to get it to work with my old GC IP2SL. However, I could never get the direct IP control working (which was my preferred configuration). Diving into that plugin, I wasn’t able to figure out what the problem was, so I started from scratch and built this one. I’m certainly not trying to create a dueling plugins situation–they seem to offer different command support and feedbacks, so I think it’s really a case of picking the one that suits your use case the best.

My primary reference was this manual, helpfully linked by @LJR here. I wasn’t able to find the original elsewhere on the internet, but hopefully it will live in github forever.

This plugin has 6 Settings that can be configured:

  • Host - the hostname or IP address to connect to (either the projector itself or a GC IP2SL)
  • Port - likely either 20554 if connecting to the projector or 4999 if connecting to the GC IP2SL
  • IPControl - TRUE or FALSE depending on whether you want to connect to the projector for direct IP control
  • ProjectorPassword - for NZ and later projectors, an 8-10 digit password is needed (must also be configured on the projector)
  • CommandDelayInterval_MS - how long to wait (in milliseconds) for a response after sending a command
  • CommandTimeoutInterval_S - how long to wait (in seconds) before treating a command as timed-out/ignored by the projector

Ideally, you shouldn’t have to change the CommandDelayInterval_MS or CommandTimeoutInterval_S values, but I’m still experimenting with them myself. Unfortunately, the projector’s command protocol is pretty brain-dead and has a lot of potential traps, where it won’t respond to commands depending on timing and how they are sequenced. This plugin jumps through a lot of hoops to make sure that there are no overlapping commands while still maintaining some responsiveness. Be aware that changing lense memories takes a long time (40+ seconds) and it is not clear the projector ever properly/usefully sends an acknowledgement. These are the commands I most frequently encounter timeouts on.

The supported MediaCommands are:

  • PowerOn / PowerOff
  • Menu
  • The standard navigation commands: DirectionUp, DirectionLeft, DirectionDown, DirectionRight, Select, and Back
  • HideToggle - toggles the hide (screen blanking) mode
  • LowLatencyOn / LowLatencyOff - turns on/off low latency mode (note, this may be problematic based on some things I’ve read elsewhere, in that other settings may also need to be changed before this will do what you want)
  • LampPowerLow / LampPowerMid / LampPowerHigh
  • EShiftOff / EShiftOn
  • MaskOn / MaskOff
  • Memory1 through Memory10 - selects respective lense memory

In addition, InputSource supports HDM1 and HDM2 to select the respective projector input.

Please let me know if there are other commands that would be useful to add.

One thing this plugin provides is a lot of feedbacks, including:

  • ProjectorModel - projector model name
  • PowerState - detailed power state (beyond the On/Off provided by the Switch Attribute)
  • SourceSignal - whether source signal is available
  • LowLatencyMode - current low latency mode
  • LampPowerMode - current lamp power mode
  • EShiftMode - current e-shift mode
  • MaskMode - current mask mode
  • CurrentMemoryMode - currently selected lens memory
  • SourceDisplayMode - source signal resolution and frame rate

I may add a few more feedbacks in the future and designed this plugin to be pretty easily extended in that regard. I’m definitely open to requests on feedbacks, as well.

Please let me know if you encounter any problems. I only have one projector (an NZ8) but have gotten things working pretty well for my setup. One additional note–usually in my plugins, I strip out the Advanced Plugin Logging capabilities to keep them “clean”. However, I run all of my plugins with that code, so I’m going to leave it in because it makes debugging so much easier when running in the app. If you don’t want it, it is pretty easy to strip out.

Change Log:

  • 2023-01-25 - original version
  • 2023-01-26 - added a workaround for a dumb behavior of the projector: if it is already on, it will ignore a command to power on, resulting in a timeout in the plugin for lack of acknowledgement :roll_eyes:
  • 2023-12-10 - added support for MaskMode feedback and MaskOn / MaskOff commands

Last updated: 2023-12-10

Here is the plugin code:
JVCProjector-2023-12-10.plugin (36.3 KB)

The plugin code itself exceeds the post character limit, so I have not repeated it here.

Good job! i thought we’d be working on the same plugin, but options are good too :slight_smile:

I had started out with that intention, but as I tried to fix whatever was causing the problem with the direct IP control, I found myself getting lost in figuring out how some of the things were working. I needed to start clean to get my thoughts in order and I’d hoped to just back-port things to your plugin, but by then they had just diverged so much in structure.

One day in and I already had to put in a workaround for the projector’s brain-dead protocol: apparently, if you send a command to power on and the projector is already on, it will just ignore it and not even acknowledge the command, resulting in the plugin hanging waiting to time out… I’m guessing it might also be the case that a power off command when it is already off could have a similar behavior, but I don’t have the energy to test that right now…

1 Like

I know the feeling…I just had a look at yours and while it looks to be so much better written than what I produced, I am going “HUH?” much of the time :slight_smile:

I would like to know how the direct IP didn’t work for you with the one I wrote, though. But seeing how long it’s taken you to get this one done, and still are low on energy, I won’t be hurt if that doesn’t happen…

I am still very unclear as to why the direct IP control didn’t work. I got my projector hung back at the beginning of December (although the room was quite a ways from ready) and I remember being excited to drop in someone else’s plugin for once :slight_smile:

I had initially wired up my GC IP2SL from my old projector, since you had indicated that the network stuff needed testing. As I recall, that worked great, but when I switched over to the direct IP control, things went downhill. I remember fighting with it just to get the password working and then I think I had a similar issue to what @Beesh mentioned, that it wouldn’t power on. I also think maybe I had some intermittent issues even when I manually powered on the projector (with navigation and the like). I spent a lot time trying to make things work, but as I read the protocol manual more closely and tried to wrap my head around how the plugin was structured, I was having a hard time finding a clear path forward.

I had also wanted to put in a number of feedbacks and started to realize the depth of the brain-dead nature of JVC’s protocol. That is what finally pushed me over the edge and to try starting from scratch. It is also probably why anyone looking at mine will say “HUH?” a lot :slight_smile:

For those that are interested in the stupidity of JVC’s protocol and some of the quirky inner workings of THR, I’ll try to lay out everything I discovered. First, the protocol. I am convinced that it was written and implemented by an electrical engineer. Only a EE could structure something so straightforward so badly. When powered off, the projector will only respond to power state requests (and power on commands, of course). All other commands will be ignored. That is, the projector will receive them, look at them, and then do absolutely nothing, even though it is fully capable of simply acknowledging that the command was received and, without much effort, even indicating it wasn’t going to do anything with it. It actually gets worse when the projector is on. It will rather rapidly acknowledge a command or request (except, as I discovered yesterday, a power on command, which it just ignores), but it may not respond to the request for a while. And while it is thinking about its response, although the diagram in the manual shows that you can send another command that will also be acknowledged right away, the reality is that it is completely unclear what will happen, but you may crash your projector if you actually try to do that–pull the power plug crash it. So, instead, what you need to do is wait for the response to come (if you sent a request). Who knows how long it will take. But you’d better wait and only have one command/request in-flight at a time, or bad things will happen.

In order to address this behavior, there is a lot of logic in my plugin to make sure that it only has one command/request in-flight at a time and that it knows whether it is expecting just an acknowledgement or if it there is a response also coming. Keeping in mind that the responses could come at any time and are basically asynchronous to anything else… Oh, and whether the projector is powered up, so that it doesn’t send anything the projector will just silently drop. :crazy_face:

Which raises the second issue I ran into, related to the inner workings of THR. Apparently, onPoll() can be interrupted at any point when you hit a button by onChangeRequest(). This is particularly problematic because, as noted above, there can be only one command/request in-flight at a time and with all of the feedbacks I wanted to support, my onPoll() could be sending a fair number of requests that take a while for the projector to respond to. Once I realized that my onChangeRequest() commands were getting in the middle of the onPoll() requests and messing them (and the projector) up, I thought: no problem, I’ll just add a flag so that when onPoll() is getting status, onChangeRequest() will patiently wait for it to finish and then send its command–the interface might seem a bit unresponsive, but it probably won’t happen that often. Well, as it turns out, onPoll() does not get to resume executing until onChangeRequest() returns. Ugh. So then I had to add logic to defer the sending of the command from onChangeRequest() so it could return and then onPoll() could finish getting status and then onPoll() sends the deferred command. I thought about getting really cute and having a global queue of commands to send and just letting onPoll() do them all, but I was worried the interface would feel too unresponsive/laggy, so I opted to just defer one command and if you’re hitting buttons so fast that additional ones interrupt the same onPoll(), you either need to display fewer feedbacks or stop being so impatient–onChangeRequest() will drop/ignore any additional commands if there is currently a deferred one (and also puts a warning in the log).

Whew. So that was my saga (for the most part) of trying to structure a plugin that would behave properly with the dumb JVC protocol and THR’s event processing quirks.

That is a long way to say that I’m not sure what the particular cause of the problem I was having with your plugin was, but it was likely a combination of the above. And once I’d gotten mine working, as I said, the structure had just diverged so much I didn’t see any way to backport it to just have a single plugin.

Hope that answers at least a few questions :slight_smile:

1 Like

I’ve updated the plugin to support turning the mask on/off (along with a new attribute so you can check the mask state). This is particularly useful if you have a cinemascope screen and are watching a movie with different aspect ratios (for example, Top Gun Maverick). This way you don’t have to have the mask turned on in the particular lens memory and can just turn it on for the movies that need it. I find this useful because I don’t mind using the overshoot to navigate plex, but I hate it when the aspect ratio changes in the movies.