A library for communicating with PJLink-compatible projectors over TCP/IP. see https://pjlink.jbmia.or.jp/english/ for information on PJLink and compatible devices.
A Common Lisp library for interfacing with PJLink standard compliant devices.
PJLink projectors operate over TCP/IP.
Table of Contents
- Query power status and power projector on/off
- Query current and available inputs, and set active input
- Query and set audio-video mute status
- Query error status, for projector components (fan, lamp, etc)
- Get lamp information (usage time, on/off)
- Query projector, manufacturer, and product names.
- LAN Projector discovery.
- Projector status notifications.
- Serial version query.
- Resolution query.
- Lamp and filter model numbers.
- Microphone and speaker control.
- Projector "freeze" control
- Software version information.
Code examples have
*ip* bound to the IP of the projector, eg
;; Get power status (pjlink:get-power-status *ip*) ;; => :STANDBY ;; Turn projector on (pjlink:power-on *ip*) (pjlink:get-power-status *ip*) ;; => :WARM-UP ;; After some time (pjlink:get-power-status *ip*) ;; => :LAMP-ON ;; Query available inputs (pjlink:get-inputs *ip*) ;; => ((:RGB . 1) (:VIDEO . 2) (:DIGITAL . 3) (:STORAGE . 4) (:RGB . 2) (:VIDEO . 6) (:DIGITAL . 7)) ;; Set the input to one of them (pjlink:set-input '(:video . 6) *ip*) ;; Get the currently active input (pjlink:get-input *ip*) ;; => (:VIDEO . 6)
In addition to parameters for the commands themselves and the host designator, all of the PJlink commands accept a 'host designator' and the following keyword parameters:
- port - Port to connect to. Defaults to PJLink port (4352)
- password - Password to use if authentication is required
- local-hort - Local interface to use for the connection
- local-port - Local port to use for the connection
Each of these parameters has a corresponding generic function you may specialize in order to make it easier to pass along similar parameters, or to customize eg password retrieval:
(defclass my-pjlink-host () ()) (defmethod pjlink:host ((obj my-pjlink-host)) *ip*) (defmethod pjlink:password ((obj my-pjlink-host)) "Get the password by reading it from the user" (read-password-from-user)) ;; ... (pjlink:power-on (make-instance 'my-pjlink-host))
pjlink-config is provided as a simple container:
(let ((config (make-instance 'pjlink:pjlink-config :host *ip* :password "JBMIAProjectorLink))) ;;Set the input to one of the available ones (let ((inputs (pjlink:get-inputs config))) (pjlink:set-input* (first inputs) config)) ;;Power off the projector (pjlink:power-off config))
Class 2 the ability to query the LAN for projectors and receive notifications.
Projectors on the LAN can be queried by using the class 2 search procedure.
Note: It is highly recommend to specify an interface (network card) to conduct the search, as it is based on UDP broadcast and will not route properly otherwise.
Note: This is a synchronous, 30 second call as per the spec
Note: Because the search procedure uses the same port as status notification, currently there's no way to conduct a search while a status listener is active.
(pjlink:search-projectors :local-host *interface-address*) ;; 30 seconds later ;; => ((#(192 168 2 5) . #(188 95 244 185 102 171)) (#(192 168 2 5) . #(10 0 39 0 0 14)))
The return value here is a list of projector IP's and mac-addresses
status-listener can be created to asynchronously monitor projector status. These updates have to be configured on the projector in a model-specific fashion so that they notify a host of status updates via UDP.
Creating and starting a listener will create a background worker thread to monitor this socket and signal an event when a status update occurs:
Note: It is highly recommended to specify an interface (network card) to listen on, rather than using the 'default' network interface.
(defun print-pjlink-event (remote-host event-type args) (format t "Received notification from ~A: ~A = ~A" remote-host event-type args)) ;; When 'handlers' is provided, listener is started automatically (defparameter *listener* (pjlink:make-status-listener :local-host *interface-address* :handlers #'print-pjlink-event)) ;; Otherwise call (pjlink:start-listener *listener*) ;; When finished listening for events: (pjlink:stop-listener *listener*) ;; Can always resume listening later: (pjlink:start-listening *listener*)