Skip to content

Commit

Permalink
fix: use different loupedeck library
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Oct 9, 2022
1 parent 939b5b4 commit c589404
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 123 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@
"dependencies": {
"@elgato-stream-deck/node": "^5.6.0",
"@julusian/jpeg-turbo": "^2.0.0",
"@loupedeck/node": "^0.1.0",
"@xencelabs-quick-keys/node": "^0.4.0",
"electron-about-window": "^1.15.2",
"electron-prompt": "^1.7.0",
"electron-store": "^8.1.0",
"eventemitter3": "^4.0.7",
"exit-hook": "^2.2.1",
"infinitton-idisplay": "^1.1.2",
"loupedeck": "github:julusian/loupedeck#master",
"meow": "^9.0.0",
"node-hid": "github:julusian/node-hid#v2.1.2-1",
"sharp": "^0.31.1",
Expand Down Expand Up @@ -124,7 +124,7 @@
"artifactName": "companion-satellite-x64.exe"
},
"linux": {
"target": "tar.gz",
"target": "dir",
"artifactName": "companion-satellite-${arch}.tar.gz",
"extraFiles": [
{
Expand Down
162 changes: 85 additions & 77 deletions src/device-types/loupedeck.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LoupedeckDevice, LoupedeckButtonId, LoupedeckKnobId } from 'loupedeck'
import { LoupedeckDevice, LoupedeckDisplayId, LoupedeckBufferFormat, LoupedeckModelId } from '@loupedeck/node'
import sharp = require('sharp')
import { CompanionSatelliteClient } from '../client'
import { CardGenerator } from '../cards'
Expand Down Expand Up @@ -30,10 +30,9 @@ export class LoupedeckWrapper implements WrappedDevice {
this.#deviceId = deviceId
this.#cardGenerator = cardGenerator

if (device.modelId !== LoupedeckModelId.LoupedeckLive) throw new Error('Incorrect model passed to wrapper!')
this.#queueOutputId = 0

this.#cardGenerator

this.#queue = new ImageWriteQueue(async (key: number, buffer: Buffer) => {
if (key > 40) {
return
Expand All @@ -46,7 +45,7 @@ export class LoupedeckWrapper implements WrappedDevice {
const boundaryWidth = width + keyPadding * 2
const boundaryHeight = height + keyPadding * 2

let newbuffer: Buffer | null = null
let newbuffer: Buffer
try {
newbuffer = await sharp(buffer, { raw: { width: 72, height: 72, channels: 3 } })
.resize(width, height)
Expand All @@ -68,17 +67,19 @@ export class LoupedeckWrapper implements WrappedDevice {
this.#isShowingCard = false

// Do a blank of the whole panel before drawing a button, so that there isnt any bleed
await this.blankDevice()
await this.blankDevice(true)
}

await this.#deck.drawBuffer({
id: 'center',
await this.#deck.drawBuffer(
LoupedeckDisplayId.Center,
newbuffer,
LoupedeckBufferFormat.RGB,
width,
height,
x: x + keyPadding,
y: y + keyPadding,
buffer: newbuffer,
})
x + keyPadding,
y + keyPadding,
true
)
} catch (e_1) {
console.error(`device(${deviceId}): fillImage failed: ${e_1}`)
}
Expand All @@ -101,55 +102,62 @@ export class LoupedeckWrapper implements WrappedDevice {
this.#deck.close()
}
async initDevice(client: CompanionSatelliteClient, status: string): Promise<void> {
const convertButtonId = (id: LoupedeckButtonId | LoupedeckKnobId): number => {
if (!isNaN(Number(id))) {
return 24 + Number(id)
} else if (id === 'circle') {
return 24
} else if (id === 'knobTL') {
return 1
} else if (id === 'knobCL') {
return 9
} else if (id === 'knobBL') {
return 17
} else if (id === 'knobTR') {
return 6
} else if (id === 'knobCR') {
return 14
} else if (id === 'knobBR') {
return 22
} else {
// Discard
return 99
const convertButtonId = (type: 'button' | 'rotary', id: number): number => {
if (type === 'button' && id >= 0 && id < 8) {
return 24 + id
} else if (type === 'rotary') {
switch (id) {
case 0:
return 1
case 1:
return 9
case 2:
return 17
case 3:
return 6
case 4:
return 14
case 5:
return 22
}
}

// Discard
return 99
}
console.log('Registering key events for ' + this.deviceId)
this.#deck.on('down', ({ id }) => client.keyDown(this.deviceId, convertButtonId(id)))
this.#deck.on('up', ({ id }) => client.keyUp(this.deviceId, convertButtonId(id)))
this.#deck.on('rotate', ({ id, delta }) => {
this.#deck.on('down', (info) => client.keyDown(this.deviceId, convertButtonId(info.type, info.index)))
this.#deck.on('up', (info) => client.keyUp(this.deviceId, convertButtonId(info.type, info.index)))
this.#deck.on('rotate', (info, delta) => {
if (info.type !== 'rotary') return

let id2
if (id === 'knobTL') {
id2 = 0
} else if (id === 'knobCL') {
id2 = 8
} else if (id === 'knobBL') {
id2 = 16
} else if (id === 'knobTR') {
id2 = 7
} else if (id === 'knobCR') {
id2 = 15
} else if (id === 'knobBR') {
id2 = 23
switch (info.index) {
case 0:
id2 = 0
break
case 1:
id2 = 8
break
case 2:
id2 = 16
break
case 3:
id2 = 7
break
case 4:
id2 = 15
break
case 5:
id2 = 23
break
}

if (id2 !== undefined) {
switch (delta) {
case -1:
client.keyUp(this.deviceId, id2)
break
case 1:
client.keyDown(this.deviceId, id2)
break
if (delta < 0) {
client.keyUp(this.deviceId, id2)
} else if (delta > 0) {
client.keyDown(this.deviceId, id2)
}
}
})
Expand Down Expand Up @@ -185,32 +193,30 @@ export class LoupedeckWrapper implements WrappedDevice {
async setBrightness(percent: number): Promise<void> {
this.#deck.setBrightness(percent / 100)
}
async blankDevice(): Promise<void> {
for (let i = 0; i < 8; i++) {
this.#deck.setButtonColor({
id: i === 0 ? 'circle' : ((i + '') as any),
color: '#0000',
})
}
async blankDevice(skipButtons?: boolean): Promise<void> {
// Hopefully this fixes the issue with the display getting stuck
// await this.#deck.getSerialNumber()

// Clear the center screen
await this.#deck.drawBuffer({
id: 'center',
width: screenWidth,
height: screenHeight,
x: 0,
y: 0,
buffer: Buffer.alloc(screenWidth * screenHeight * 3),
})
await this.#deck.blankDevice(true, !skipButtons)

// Hopefully this fixes the issue with the display getting stuck
// await this.#deck.getSerialNumber()
}
async draw(d: DeviceDrawProps): Promise<void> {
if (d.keyIndex >= 24 && d.keyIndex < 32) {
const index = d.keyIndex - 24
const color = d.color || '#000000'

const red = d.color ? parseInt(d.color.substr(1, 2), 16) : 0
const green = d.color ? parseInt(d.color.substr(3, 2), 16) : 0
const blue = d.color ? parseInt(d.color.substr(5, 2), 16) : 0

this.#deck.setButtonColor({
id: index === 0 ? 'circle' : ((index + '') as any),
color,
id: index,
red,
green,
blue,
})

return
}
const x = (d.keyIndex % 8) - 2
Expand Down Expand Up @@ -239,14 +245,16 @@ export class LoupedeckWrapper implements WrappedDevice {
if (outputId === this.#queueOutputId) {
this.#isShowingCard = true
// still valid
this.#deck.drawBuffer({
id: 'center',
await this.#deck.drawBuffer(
LoupedeckDisplayId.Center,
buffer,
x: keyPadding,
y: keyPadding,
LoupedeckBufferFormat.RGB,
width,
height,
})
keyPadding,
keyPadding,
true
)
}
})
.catch((e) => {
Expand Down
17 changes: 6 additions & 11 deletions src/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { QuickKeysWrapper } from './device-types/xencelabs-quick-keys'
import Infinitton = require('infinitton-idisplay')
import { InfinittonWrapper } from './device-types/infinitton'
import { LoupedeckWrapper } from './device-types/loupedeck'
import { listDevices as listLoupedecks, LoupedeckDevice } from 'loupedeck'
import * as HID from 'node-hid'
import { openLoupedeck, listLoupedecks, LoupedeckDevice, LoupedeckModelId } from '@loupedeck/node'

// Force into hidraw mode
HID.setDriverType('hidraw')
Expand Down Expand Up @@ -225,11 +225,11 @@ export class DeviceManager {
console.error(`Quick keys scan failed: ${e}`)
})

listLoupedecks({ ignoreWebsocket: true })
listLoupedecks()
.then((devs) => {
for (const dev of devs) {
if (dev.type === 'serial' && dev.serialNumber) {
this.tryAddLoupedeck(dev.path, dev.serialNumber)
if (dev.serialNumber && dev.model === LoupedeckModelId.LoupedeckLive) {
this.tryAddLoupedeckLive(dev.path, dev.serialNumber)
}
}
})
Expand All @@ -238,23 +238,18 @@ export class DeviceManager {
})
}

private async tryAddLoupedeck(path: string, serial: string) {
private async tryAddLoupedeckLive(path: string, serial: string) {
let ld: LoupedeckDevice | undefined
try {
if (!this.devices.has(serial)) {
console.log(`adding new device: ${path}`)
console.log(`existing = ${JSON.stringify(Array.from(this.devices.keys()))}`)

ld = new LoupedeckDevice({ path, autoConnect: false })
ld.on('disconnect', () => {
console.error('device disconnect')
this.cleanupDeviceById(serial)
})
ld = await openLoupedeck(path)
ld.on('error', (err) => {
console.error('device error', err)
this.cleanupDeviceById(serial)
})
await ld.connect()

const devInfo = new LoupedeckWrapper(serial, ld, this.cardGenerator)
await this.tryAddDeviceInner(serial, devInfo)
Expand Down
42 changes: 9 additions & 33 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@
node-addon-api "^5.0.0"
pkg-prebuilds "~0.1.0"

"@loupedeck/node@^0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@loupedeck/node/-/node-0.1.0.tgz#8e636e4adb34c7a79dff0a2a31c18af2a411ab6c"
integrity sha512-V1S5yMxVMKnSLKHrICrbGAGJIeAvmlz8bLuoZeJ29nhJMTPb8DEHjxw8VZzLhneK5HA3g73PBqOtJYB4S73ZAA==
dependencies:
eventemitter3 "^4.0.7"
serialport "^10.4.0"
tslib "^2.4.0"

"@malept/cross-spawn-promise@^1.1.0":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d"
Expand Down Expand Up @@ -1112,26 +1121,6 @@ color-name@^1.0.0, color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==

color-parse@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/color-parse/-/color-parse-1.4.2.tgz#78651f5d34df1a57f997643d86f7f87268ad4eb5"
integrity sha512-RI7s49/8yqDj3fECFZjUI1Yi0z/Gq1py43oNJivAIIDSyJiOZLfYCRQEgn8HEVAj++PcRe8AnL2XF0fRJ3BTnA==
dependencies:
color-name "^1.0.0"

color-rgba@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/color-rgba/-/color-rgba-2.4.0.tgz#ae85819c530262c29fc2da129fc7c8f9efc57015"
integrity sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q==
dependencies:
color-parse "^1.4.2"
color-space "^2.0.0"

color-space@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/color-space/-/color-space-2.0.0.tgz#ae7813abcbe3dabda9e3e2266b0675f688b24977"
integrity sha512-Bu8P/usGNuVWushjxcuaGSkhT+L2KX0cvgMGMTF0KJ7lFeqonhsntT68d6Yu3uwZzCmbF7KTB9EV67AGcUXhJw==

color-string@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
Expand Down Expand Up @@ -3070,14 +3059,6 @@ log-update@^4.0.0:
slice-ansi "^4.0.0"
wrap-ansi "^6.2.0"

"loupedeck@github:julusian/loupedeck#master":
version "1.3.3-julusian.0"
resolved "https://codeload.github.com/julusian/loupedeck/tar.gz/d5ec7c1951b8767c80193bc5e387aad1e0eb0733"
dependencies:
color-rgba "^2.4.0"
serialport "^10.4.0"
ws "^8.6.0"

lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
Expand Down Expand Up @@ -4949,11 +4930,6 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==

ws@^8.6.0:
version "8.9.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.9.0.tgz#2a994bb67144be1b53fe2d23c53c028adeb7f45e"
integrity sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==

xmlbuilder@>=11.0.1, xmlbuilder@^15.1.1:
version "15.1.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
Expand Down

0 comments on commit c589404

Please sign in to comment.