diff options
author | Determinant <[email protected]> | 2024-08-14 22:13:43 -0700 |
---|---|---|
committer | Determinant <[email protected]> | 2024-08-14 22:13:43 -0700 |
commit | 2e301c67e21618bf3fd6d4bc1c70e569ba599cee (patch) | |
tree | 4533e51952031304fb364e5947a9074f7a88e6d4 | |
parent | 21c23c6cf4b8fe12ffa32030e70c0f71f18feae7 (diff) |
get loupedeck to work with X-Plane
-rwxr-xr-x[-rw-r--r--] | ctrl.mjs (renamed from index.mjs) | 69 | ||||
-rw-r--r-- | profile.yaml | 221 | ||||
-rw-r--r-- | xplane.mjs | 37 |
3 files changed, 278 insertions, 49 deletions
@@ -1,3 +1,4 @@ +#!/usr/bin/env node import { registerFont } from 'canvas'; registerFont('./ocr-a-ext.ttf', { family: 'ocr' }); @@ -6,25 +7,48 @@ import { discover } from 'loupedeck' import { readFile } from 'fs/promises'; import { parse } from 'yaml' +import { sendCommand } from './xplane.mjs'; +import { isNumberObject } from 'util/types'; const pages = parse(await readFile("./profile.yaml", "utf8")); +// state of the controller +let currentPage; +let pressed = new Set(); +let highlighted = new Set(); + // Detects and opens first connected device const device = await discover() +const isNumber = (x) => { return !isNaN(x); } + const isObject = (obj) => { return obj != null && obj.constructor.name === "Object" }; +const doAction = (labeled, type) => { + if (!isObject(labeled)) { + return; + } + let actionSpec = labeled[type]; + if (actionSpec === undefined) { + return; + } + if (actionSpec.hasOwnProperty('xplane_cmd')) { + sendCommand(actionSpec.xplane_cmd); + } +}; + const rectifyLabel = (label) => { let text; - let font; + let font = "22px ocr"; if (isObject(label)) { text = label.text; - font = `${label.size}px ocr`; + if (label.hasOwnProperty('size')) { + font = `${label.size}px ocr`; + } } else { text = label.toString(); - font = "24px ocr"; } return { text, font } } @@ -95,10 +119,6 @@ const loadPage = (page) => { } }; -let currentPage; -let pressed = new Set(); -let highlighted = new Set(); - // Observe connect events device.on('connect', () => { console.info('Connection successful!') @@ -108,12 +128,32 @@ device.on('connect', () => { // React to button presses device.on('down', ({ id }) => { - console.info(`switch to page: ${id}`) - if (id == 0) { - return + if (isNumber(id)) { + console.info(`switch to page: ${id}`) + if (id == 0) { + return + } + currentPage = id; + loadPage(pages[currentPage]); + } else { + const { left, right, keys } = pages[currentPage] || {}; + if (!left) { + return + } + let pos = {"T": 0, "C": 1, "B": 2}[id.substring(4, 5)]; + let side = {"L": ['left', left], "R": ['right', right]}[id.substring(5, 6)]; + let mask = [false, false, false]; + mask[pos] = true; + drawSideKnobs(side[0], side[1], mask); + if (!highlighted.has(id)) { + highlighted.add(id); + setTimeout(() => { + drawSideKnobs(side[0], side[1], [false, false, false]); + highlighted.delete(id); + }, 200); + } + doAction(side[1][pos], 'pressed'); } - currentPage = id; - loadPage(pages[currentPage]); }) // React to knob turns @@ -134,6 +174,7 @@ device.on('rotate', ({ id, delta }) => { highlighted.delete(id); }, 200); } + doAction(side[1][pos], delta > 0 ? 'inc' : 'dec'); }) const clearStaleButton = (touches) => { @@ -153,7 +194,9 @@ device.on('touchstart', ({ changedTouches, touches }) => { return } pressed.add(target.key); - drawKey(target.key, pages[currentPage].keys[target.key], true); + const key = pages[currentPage].keys[target.key]; + drawKey(target.key, key, true); + doAction(key, 'pressed'); //device.vibrate() }) diff --git a/profile.yaml b/profile.yaml index a7f3210..07f1e33 100644 --- a/profile.yaml +++ b/profile.yaml @@ -3,49 +3,198 @@ # page 1 - left: # left knob labels - - HDG - - ALT - - CRS - right: - # right knob labels + - text: FMS out + size: 20 + inc: + xplane_cmd: sim/GPS/g1000n1_fms_outer_up + dec: + xplane_cmd: sim/GPS/g1000n1_fms_outer_down - text: FMS in size: 20 + inc: + xplane_cmd: sim/GPS/g1000n1_fms_inner_up + dec: + xplane_cmd: sim/GPS/g1000n1_fms_inner_down + - text: BARO + inc: + xplane_cmd: sim/GPS/g1000n1_baro_up + dec: + xplane_cmd: sim/GPS/g1000n1_baro_down + right: + # right knob labels - text: FMS out size: 20 - - BARO + inc: + xplane_cmd: sim/GPS/g1000n2_fms_outer_up + dec: + xplane_cmd: sim/GPS/g1000n2_fms_outer_down + - text: FMS in + size: 20 + inc: + xplane_cmd: sim/GPS/g1000n2_fms_inner_up + dec: + xplane_cmd: sim/GPS/g1000n2_fms_inner_down + - '' keys: # 12 touchable keys from left to right, top to bottom - - A - - B - - C - - D - - E - - F - - G - - H - - I - - J - - K - - L + - text: -D-> + pressed: + xplane_cmd: sim/GPS/g1000n1_direct + - text: MENU + pressed: + xplane_cmd: sim/GPS/g1000n1_menu + - text: Direct + pressed: + xplane_cmd: sim/GPS/g1000n2_direct + - text: MENU + pressed: + xplane_cmd: sim/GPS/g1000n2_menu + - text: FPL + pressed: + xplane_cmd: sim/GPS/g1000n1_fpl + - text: PROC + pressed: + xplane_cmd: sim/GPS/g1000n1_proc + - text: FPL + pressed: + xplane_cmd: sim/GPS/g1000n2_fpl + - text: PROC + pressed: + xplane_cmd: sim/GPS/g1000n2_proc + - text: CLR + pressed: + xplane_cmd: sim/GPS/g1000n1_clr + - text: ENT + pressed: + xplane_cmd: sim/GPS/g1000n1_ent + - text: CLR + pressed: + xplane_cmd: sim/GPS/g1000n2_clr + - text: ENT + pressed: + xplane_cmd: sim/GPS/g1000n2_ent + # page 2 - left: - - A - - B - - C + - text: HDG + inc: + xplane_cmd: sim/GPS/g1000n1_hdg_up + dec: + xplane_cmd: sim/GPS/g1000n1_hdg_down + pressed: + xplane_cmd: sim/GPS/g1000n1_hdg_sync + - text: ALT + inc: + xplane_cmd: sim/GPS/g1000n1_alt_inner_up + dec: + xplane_cmd: sim/GPS/g1000n1_alt_inner_down + - text: CRS + inc: + xplane_cmd: sim/GPS/g1000n1_crs_up + dec: + xplane_cmd: sim/GPS/g1000n1_crs_down + pressed: + xplane_cmd: sim/GPS/g1000n1_crs_sync + right: - - X - - Y - - Z + - '' + - '' + - '' + keys: + - text: AP + pressed: + xplane_cmd: sim/GPS/g1000n1_ap + - text: FD + pressed: + xplane_cmd: sim/GPS/g1000n1_fd + - text: HDG + pressed: + xplane_cmd: sim/GPS/g1000n1_hdg + - text: ALT + pressed: + xplane_cmd: sim/GPS/g1000n1_alt + - text: NAV + pressed: + xplane_cmd: sim/GPS/g1000n1_nav + - text: VNV + pressed: + xplane_cmd: sim/GPS/g1000n1_vnv + - text: APR + pressed: + xplane_cmd: sim/GPS/g1000n1_apr + - text: BC + pressed: + xplane_cmd: sim/GPS/g1000n1_bc + - text: VS + pressed: + xplane_cmd: sim/GPS/g1000n1_vs + - text: UP + pressed: + xplane_cmd: sim/GPS/g1000n1_nose_up + - text: FLC + pressed: + xplane_cmd: sim/GPS/g1000n1_flc + - text: DN + pressed: + xplane_cmd: sim/GPS/g1000n1_nose_down + +# page 3 +- left: + - text: COM in + size: 20 + inc: + xplane_cmd: sim/GPS/g1000n1_com_inner_up + dec: + xplane_cmd: sim/GPS/g1000n1_com_inner_down + pressed: + xplane_cmd: sim/GPS/g1000n1_com12 + - text: COM out + size: 20 + inc: + xplane_cmd: sim/GPS/g1000n1_com_outer_up + dec: + xplane_cmd: sim/GPS/g1000n1_com_outer_down + pressed: + xplane_cmd: sim/GPS/g1000n1_com12 + - '' + right: + - text: NAV in + size: 20 + inc: + xplane_cmd: sim/GPS/g1000n1_nav_inner_up + dec: + xplane_cmd: sim/GPS/g1000n1_nav_inner_down + pressed: + xplane_cmd: sim/GPS/g1000n1_nav12 + - text: NAV out + size: 20 + inc: + xplane_cmd: sim/GPS/g1000n1_nav_outer_up + dec: + xplane_cmd: sim/GPS/g1000n1_nav_outer_down + pressed: + xplane_cmd: sim/GPS/g1000n1_nav12 + - '' keys: - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 + - text: <-COM + pressed: + xplane_cmd: sim/GPS/g1000n1_com_ff + - text: <-NAV + pressed: + xplane_cmd: sim/GPS/g1000n1_nav_ff + - '' + - '' + - text: COM1 M + size: 16 + - text: COM1 + pressed: + xplane_cmd: sim/GPS/monitor_audio_com1 + - '' + - '' + - text: COM2 M + size: 16 + - text: COM2 + pressed: + xplane_cmd: sim/GPS/monitor_audio_com2 + - '' + - '' diff --git a/xplane.mjs b/xplane.mjs new file mode 100644 index 0000000..54190c9 --- /dev/null +++ b/xplane.mjs @@ -0,0 +1,37 @@ +import dgram from 'node:dgram'; + +const xplaneAddr = "localhost"; +const xplanePort = 49000; +const socket = dgram.createSocket('udp4'); + +//socket.on('message', (msg, rinfo) => { +// console.log(msg, rinfo) +//}); +// +//socket.bind(10080); + +const subscribeDataRef = async (dataRef) => { + //const dataRef = "sim/flightmodel/position/indicated_airspeed"; + let buffer = Buffer.alloc(4 + 1 + 4 * 2 + 400); + let off = buffer.write("RREF"); + off = buffer.writeUInt8(0, off); // null terminated + off = buffer.writeInt32LE(1, off); // xint frequency + off = buffer.writeInt32LE(0, off); // xint client + off += buffer.write(dataRef, off); // char[400] dataref + off = buffer.writeUInt8(0, off); // null terminated + console.log(Array.from(buffer)); + await socket.send(buffer, 0, buffer.length, xplanePort, xplaneAddr); +} + + +export const sendCommand = async (cmd) => { + let buffer = Buffer.alloc(4 + 1 + cmd.length + 1); + let off = buffer.write("CMND"); + off = buffer.writeUInt8(0, off); // null terminated + off += buffer.write(cmd, off); // command + off = buffer.writeUInt8(0, off); // null terminated + console.info(`sent command: ${cmd}`) + await socket.send(buffer, 0, buffer.length, xplanePort, xplaneAddr); +}; + +//command("sim/GPS/g1000n1_hdg_down"); |