Last active
May 1, 2020 19:27
-
-
Save alexanderson1993/086c324fa83094b735b40030016eb270 to your computer and use it in GitHub Desktop.
A React hook which uses the WebUSB API to send DMX messages to a connected ENTTEC Pro device
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A React hook which uses the WebUSB API to send DMX messages to a connected ENTTEC Pro device | |
// This will only work in Chrome. | |
// If you want to just try it, run it through the TypeScript compiler here https://www.typescriptlang.org/play/index.html | |
import React from "react"; | |
declare global { | |
interface Navigator { | |
// This USB type comes from @types/w3c-web-usb | |
readonly usb: USB; | |
} | |
} | |
interface DMXDevice { | |
ready: boolean; | |
close: () => Promise<void>; | |
update: (channels: number[]) => Promise<USBOutTransferResult | void>; | |
} | |
async function setUpLightingDevice(): Promise<DMXDevice> { | |
const lightingDevice = await navigator.usb.requestDevice({ | |
filters: [{vendorId: 1027, productId: 24577}], | |
}); | |
await lightingDevice.open(); | |
await lightingDevice.claimInterface(0); | |
lightingDevice.controlTransferOut({ | |
// It's a USB class request | |
requestType: "class", | |
// The destination of this request is the interface | |
recipient: "interface", | |
// CDC: Communication Device Class | |
// 0x22: SET_CONTROL_LINE_STATE | |
// RS-232 signal used to tell the USB device that the computer is now present. | |
request: 0x22, | |
// Yes | |
value: 0x01, | |
// Interface #2 | |
index: 0x00, | |
}); | |
const universe = new Array(512).fill(0); | |
// This only supports ENTTEC Pro devices | |
const ENTTEC_PRO_DMX_STARTCODE = 0x00; | |
const ENTTEC_PRO_START_OF_MSG = 0x7e; | |
const ENTTEC_PRO_END_OF_MSG = 0xe7; | |
const ENTTEC_PRO_SEND_DMX_RQ = 0x06; | |
const header = [ | |
ENTTEC_PRO_START_OF_MSG, | |
ENTTEC_PRO_SEND_DMX_RQ, | |
universe.length & 0xff, | |
(universe.length >> 8) & 0xff, | |
ENTTEC_PRO_DMX_STARTCODE, | |
]; | |
// Initialize a blank universe | |
lightingDevice.transferOut( | |
2, | |
Uint8Array.from([...header, ...universe, ENTTEC_PRO_END_OF_MSG]), | |
); | |
return { | |
ready: true, | |
close: () => lightingDevice.close(), | |
update: (channels: number[]) => { | |
// Make sure we're sending 512 channels | |
const output: number[] = []; | |
for (let i = 0; i < 512; i++) { | |
output[i] = channels[i] || 0; | |
} | |
// Send the message | |
return lightingDevice.transferOut( | |
2, | |
Uint8Array.from([...header, ...universe, ENTTEC_PRO_END_OF_MSG]), | |
); | |
}, | |
}; | |
} | |
async function noop() {} | |
export function useUSBDMX(): DMXDevice { | |
const [lightingDevice, setLightingDevice] = React.useState<DMXDevice>({ | |
close: noop, | |
update: noop, | |
ready: false, | |
}); | |
React.useEffect(() => { | |
let internalLightingDevice: DMXDevice; | |
setUpLightingDevice().then(res => { | |
internalLightingDevice = res; | |
setLightingDevice(res); | |
}); | |
return () => { | |
internalLightingDevice?.close(); | |
}; | |
}, []); | |
return lightingDevice; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment