Thursday, March 30, 2017

PJLink - network control for projector

PJLink is a protocol developed by JBMIA for controlling projectors (and presentation displays for example from NEC) via LAN connections
The protocol works over TCP/IP, by default it use port 4352. Protocol supports authentification, however as I didn't set password for PJLink on my NEC, it works without authentification. (Not sure if that would be same on other venodrs as well).
It use get and set commands. You can get or set several thinks, Power on/off or get the status. Input for video and audio source. Volume and if it's projector you can work also with lens and apertur.
The structure is prety easy header followed by command and parametr:

HeaderClassCommandSpaceParameter
%1POWR?

Detail protocol specification: http://pjlink.jbmia.or.jp/english/data/5-1_PJLink_eng_20131210.pdf
More documentation can be find here: http://pjlink.jbmia.or.jp/english/

Below examples and test was done on my NEC MultiSync V423, PJLink listen on port 4352 as standard suggest the other ports are 80 http for other settings and 7142 which is NEC prorietary remote control protocol, more can be found here.

root@raspberrypi:/home/pi# nmap -sT 10.0.0.40 -p 1-65535

Starting Nmap 6.00 ( http://nmap.org ) at 2017-03-24 21:00 CET
Nmap scan report for 10.0.0.40
Host is up (0.018s latency).
Not shown: 65532 closed ports
PORT     STATE SERVICE
80/tcp   open  http
4352/tcp open  unknown
7142/tcp open  unknown
MAC Address: 58:C2:32:97:3A:48 (Unknown)

Below are example use telnet to switch on the device, switch it off and get status:

Connection closed by foreign host.
root@raspberrypi:/home/pi# telnet 10.0.0.40 4352
Trying 10.0.0.40...
Connected to 10.0.0.40.
Escape character is '^]'.
PJLINK 0
%1POWR 1
%1POWR=OK
%1POWR 0
%1POWR=OK
%1POWR ?
%1POWR=0
^]
telnet> quit
Connection closed.

Here is Python example for Power On/Off
#! /usr/bin/python

#takes two arguments IP and Power ON|OFF
#example: ./PJLinkPower.py 10.0.0.40 OFF

import sys
import cgi
import socket

if len(sys.argv) == 1:
  form = cgi.FieldStorage()
  monitor_ip = form.getvalue('monitor_ip')
else:
  monitor_ip = sys.argv[1]
  power = sys.argv[2]

#define some constanst to make life easier
port = 4352
buffer_size = 1024

if (power == 'ON'):
  command = '%1POWR 1\r'
elif (power == 'OFF'):
  command = '%1POWR 0\r'
else:
  command = '%1POWR ?\r'
  print "Invalid command."
  print "Use: IP ON|OFF"
  print "getting current status of device:"

new = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
new.connect((monitor_ip, port))
recv_data = new.recv(buffer_size)
print recv_data
print "sending: ", command
new.send(command)
recv_data = new.recv(buffer_size)
print recv_data
new.close()

Here is example of use:
root@raspberrypi:/var/www/cgi-bin# ./PJLinkPower.py 10.0.0.40 ON
PJLINK 0
sending:  %1POWR 1
%1POWR=OK
root@raspberrypi:/var/www/cgi-bin# ./PJLinkPower.py 10.0.0.40 OFF
PJLINK 0
sending:  %1POWR 0
%1POWR=OK
root@raspberrypi:/var/www/cgi-bin#
root@raspberrypi:/var/www/cgi-bin# ./PJLinkPower.py 10.0.0.40 TEST
Invalid command.
Use: IP ON|OFF
getting current status of device:
PJLINK 0
sending:  %1POWR ?
%1POWR=0

No comments: