Edit: I guess the short version is: are there ways to send Hex commands to network devices?
Hi All,
I have an issue wich i hope someone can help me with.
I found one other post with a similar question, but it was closed after a year, without any replay.
I recently added a JVC DLA-NP5 to my list of devices i want to control with hubitat.
I'm not able to find a standard driver, so i decided to code one myself.
I have some experience with coding, but not too much, so I figured I used chatGPT as starting point.
The JVC requires a handshake to be able to control it.
After a lot of misfires, I now have the handshake working.
But sending actual commands doesn't work (yet)
I have tested if it could work on a mac, using socat in a terminal window.
user@Macbook ~ % (
# Eerst de handshake
PJREQ printf 'PJREQ'
sleep 0.3
# Daarna direct Power On commando in hex
printf '\x21\x89\x01\x50\x57\x31\x0A'
) | socat -x -v - TCP:"ipnumberhere":20554
zsh: command not found: #
> 2026/01/12 00:15:13.000110220 length=5 from=0 to=4
50 4a 52 45 51 PJREQ
--
< 2026/01/12 00:15:13.000123972 length=5 from=0 to=4
50 4a 5f 4f 4b PJ_OK
--
PJ_OK< 2026/01/12 00:15:13.000128178 length=5 from=5 to=9
50 4a 41 43 4b PJACK
--
PJACKzsh: command not found: #
> 2026/01/12 00:15:13.000412562 length=7 from=5 to=11
21 89 01 50 57 31 0a !..PW1.
--
< 2026/01/12 00:15:13.000415301 length=6 from=10 to=15
06 89 01 50 57 0a ...PW.
--
?PW
This directly turned on the projector, proving there is a way to actually do it.
In hubitat i currently use interfaces.rawSocket.sendMessage(string msg)
this does work for the handshake, if the string message is simply "PJREQ" or "PJREQ/n"
however, I cant seem to get it to work for other commands.
commands are currently formed from several items
a header ("21" for operation)
a DevID (always "8901" for the NP5)
the actual Command ("505731" for turning on)
a close code (always (0A))
so in total that would make a hex command "21 89 01 50 57 31 0A"
This is also used in the socat test and worked.
However if i use this in the interfaces.rawSocket.sendMessage("2189015057310A") it doesn't work.
I have also tried to use a few alternatives
alternative 1:
private String buildCommandAlt1(String hdr, String cmdHex) {
byte[] bytes = hubitat.helper.HexUtils.hexStringToByteArray(hdr + DevID().UID + cmdHex + CloseCode().end)
return new String(bytes, "ISO-8859-1")
}
// use:
String cmd = buildCommandAlt1(Header().Operate, Commands().PowerOn)
interfaces.rawSocket.sendMessage(cmd)
no response from projector...
alternative 2:
private String buildCommandAlt2(String hdr, String cmdHex) {
byte[] bytes = hubitat.helper.HexUtils.hexStringToByteArray(hdr + DevID().UID + cmdHex + CloseCode().end)
return new String(bytes, "US-ASCII")
}
This one doesn't work since the US-ASCII is different and translates wrong
alternative 3
private String buildCommandAlt3(String hdr, String cmdHex) {
byte[] bytes = hubitat.helper.HexUtils.hexStringToByteArray(hdr + DevID().UID + cmdHex + CloseCode().end)
char[] chars = new char[bytes.length]
for (int i = 0; i < bytes.length; i++) {
chars[i] = (char)(bytes[i] & 0xFF)
}
return new String(chars)
}
Also no response from projector...
Alternative 5
interfaces.rawSocket.sendMessage("\\x21\\x89\\x01\\x50\\x57\\x31\\x0A")
Also no response from projector.
Does anyone have any other thoughts?
Are there direct hex sending possibilities available that Chat (and I) don't know about?
Are there other ways to translate or send or whatever?
ANY HELP WOULD BE HIGHLY APPRECIATED!
I'm running a Hubitat C7 with the latest software. (also running a C8-pro, but not for this issue ;))
Below full driver code, and LOG after pressing "ON" button:
my code:
///////////////////////////////////////////////////////////////////////////////
// GENERAL INFO
///////////////////////////////////////////////////////////////////////////////
// JVC D-ILA - LAN driver for Hubitat
// Usage:
// - Install as a driver, create device, set IP (and Port if different)
///////////////////////////////////////////////////////////////////////////////
// DEFINITION
///////////////////////////////////////////////////////////////////////////////
metadata {
definition (
name: "JVC D-ILA LAN control",
namespace: "community.jvc",
author: "Sgt.") {
capability "Switch"
command "Handshake" //for testing purposes
command "connectioncheck" //for testing purposes
//command "setInput", ["string"]
//command "enquiryPower"
//attribute "powerState", "string"
//attribute "input", "string"
}
preferences {
input name: "deviceIP", type: "text", title: "Projector IP address", required: true
input name: "devicePort", type: "number", title: "Port (default 20554)", defaultValue: 20554, required: true
input name: "debug", type: "bool", title: "Enable debug logging", defaultValue: true
}
}
def installed() { updated() }
def updated() {
if (deviceIP) updateDataValue("deviceIP", deviceIP.trim())
if (devicePort) updateDataValue("devicePort", devicePort.toString())
}
///////////////////////////////////////////////////////////////////////////////
// Public commands
///////////////////////////////////////////////////////////////////////////////
def Handshake() {
MakeHandshake()
}
def on() {
if(debug) log.debug "=== TURNING ON JVC NP5 ==="
state.PendingCommand = buildCommand(Header().Operate, Commands().PowerOn)
MakeHandshake()
}
def off() {
if(debug) log.debug "=== TURNING OFF JVC NP5 ==="
state.PendingCommand = buildCommand(Header().Operate, Commands().PowerOff)
MakeHandshake()
}
def connectioncheck() {
if(debug) log.debug "=== CHECKING CONNECTION ==="
state.PendingCommand = buildCommand(Header().Operate, Commands().ConnectionCheck)
MakeHandshake()
}
///////////////////////////////////////////////////////////////////////////////
// Commands / hex map
///////////////////////////////////////////////////////////////////////////////
private Map Header(){ //map the header commands
[
Operate: "21", //Operate the unit (e.g. Power on)
Status: "3F", //Request statue (e.g. is power on or off)
Response: "40", //send by the device in each response
ACK: "06" //Send by the device after each received command
]
}
private Map DevID(){ //map the device ID
[
UID: "8901" // fixed code for JVC D-ILA NP5
]
}
private Map Commands() { // map commands and datastrings
[
ConnectionCheck: "0000",
PowerOn: "505731",
PowerOff: "505730",
inputHDMI1: "495036",
inputHDMI2: "495037",
//remote codes:
//standby: "524337333036",
//on: "524337333035",
//input: "524337333038",
settingmemory: "524337334434",
lenscontrol: "524337333330",
hide: "524337333144",
info: "524337333734",
up: "524337333031",
down: "524337333032",
right: "524337333334",
left: "524337333336",
ok: "524337333246",
menu: "524337333245",
back: "524337333033",
picturemode: "524337334634",
colorprofile: "524337334634",
gammasetting: "524337334635",
cmd: "524337333841",
mpc: "524337334630",
advancedmenu: "524337333733"
]
}
private Map CloseCode() { //close the command string
[
end: "0A" //ends the sending string
]
}
///////////////////////////////////////////////////////////////////////////////
// Core helpers
///////////////////////////////////////////////////////////////////////////////
private void MakeHandshake(){
if (debug) log.debug "=== JVC HANDSHAKE START ==="
// schedule kill after 5 seconds
// runIn(5, "killConnection")
if (debug) log.debug "disconnecting existing connections"
try {interfaces.rawSocket.disconnect()}
catch (dconerr) {if (debug) log.warn "disconnecting error: ${dconerr}"}
pauseExecution(500)
if (debug) log.debug "Connecting to ${deviceIP}:${devicePort}"
try{interfaces.rawSocket.connect(deviceIP, devicePort as int)}
catch (conerr) {log.warn "connection error: ${conerr}"}
}
void parse(String message) {
if (!message) return
if (debug) log.debug "RECV RAW: ${message}"
String ascii = RawToAscii(message)
if (debug) log.debug "RECV ASCII: ${ascii}"
// CASE 1: PJ_OK → send PJREQ
if (ascii.contains("PJ_OK")) {
if (debug) log.debug ">>> PJ_OK → sending PJREQ"
String HandshakeToSend = "PJREQ\n"
if (debug) log.debug "sending ${HandshakeToSend}"
interfaces.rawSocket.sendMessage(HandshakeToSend)
return
}
// CASE 2: PJACK → send actual command
if (ascii.contains("PJACK")) {
if (debug) log.debug ">>> PJACK → ready to send pending command"
if (state.PendingCommand) {
if (debug) log.debug "Sending command ${state.PendingCommand}"
//String Bin = hexToBinary(state.PendingCommand)
//if (debug) log.debug "as Binary: ${Bin} (length = ${Bin.length()})"
interfaces.rawSocket.sendMessage(state.PendingCommand)
if (debug) log.debug "Command ${state.PendingCommand} send"
}
state.PendingCommand = null
//Bin = null
if (debug) log.warn "Command set to ${state.PendingCommand}"
return
}
// CASE 3: PJNAK → warn and stop
if (ascii.contains("PJNAK")) {
log.warn ">>> PJNAK → disconnect and exit"
interfaces.rawSocket.disconnect()
exit
}
// CASE 4: ACK → done
if (message.startsWith("06")) {
log.debug "=== COMMAND ACK RECEIVED ==="
unschedule("killConnection")
//interfaces.rawSocket.disconnect()
return
}
}
///////////////////////////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////////////////////////
private String buildCommand(String hdr, String cmdHex) {
// converteer elk hexpaar naar een byte
byte[] bytes = hubitat.helper.HexUtils.hexStringToByteArray(hdr + DevID().UID + cmdHex + CloseCode().end)
return new String(bytes, "ISO-8859-1") // of "US-ASCII" werkt ook vaak
}
private String RawToAscii(String message) {
if (!message) return null
byte[] bytes = hubitat.helper.HexUtils.hexStringToByteArray(message)
return new String(bytes, "US-ASCII")
}
//private String AsciiToHex(String s) {
// if (!s) return ""
// s.getBytes("US-ASCII").collect{ String.format("%02X", it & 0xFF) }.join("")
//}
//private String hexToBinary(String hex) {
// byte[] bytes = hubitat.helper.HexUtils.hexStringToByteArray(hex)
// return new String(bytes, "ISO-8859-1")
//}
private void killConnection() {
if (debug) log.warn "=== Killing rawSocket connection due to timeout ==="
try {interfaces.rawSocket.disconnect()}
catch (killconnerr) {log.warn "Error disconnecting socket: ${killconnerr}"}
state.PendingCommand = null
}
***the log ***
dev:852026-01-12 00:34:04.792warnCommand set to null
dev:852026-01-12 00:34:04.788debugCommand ! PW1 send
dev:852026-01-12 00:34:04.783debugSending command ! PW1
dev:852026-01-12 00:34:04.778debug>>> PJACK → ready to send pending command
dev:852026-01-12 00:34:04.773debugRECV ASCII: PJACK
dev:852026-01-12 00:34:04.768debugRECV RAW: 504A41434B
dev:852026-01-12 00:34:04.641debugsending PJREQ
dev:852026-01-12 00:34:04.634debug>>> PJ_OK → sending PJREQ
dev:852026-01-12 00:34:04.628debugRECV ASCII: PJ_OK
dev:852026-01-12 00:34:04.618debugRECV RAW: 504A5F4F4B
dev:852026-01-12 00:34:04.451debugConnecting to 192.168.20.201:20554
dev:852026-01-12 00:34:03.955debugdisconnecting existing connections
dev:852026-01-12 00:34:03.950debug=== JVC HANDSHAKE START ===
dev:852026-01-12 00:34:03.946debug=== TURNING ON JVC NP5 ===