Skip to content

Commit

Permalink
fix: uint8array
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Sep 8, 2024
1 parent 245d198 commit d27f1c8
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 48 deletions.
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type { HIDDevice, HIDDeviceInfo, HIDDeviceEvents, ChildHIDDeviceInfo } fr
export type { OpenStreamDeckOptions } from './models/base.js'
export { StreamDeckProxy } from './proxy.js'
export type { PropertiesService } from './services/properties/interface.js'
export { uint8ArrayToDataView } from './util.js'

/** Elgato vendor id */
export const VENDOR_ID = 0x0fd9
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/services/buttonsLcdDisplay/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ export class DefaultButtonsLcdService implements ButtonsLcdDisplayService {
if (this.#deviceProperties.FULLSCREEN_PANELS > 0) {
// TODO - should this be a separate property?
for (let screenIndex = 0; screenIndex < this.#deviceProperties.FULLSCREEN_PANELS; screenIndex++) {
const buffer = Buffer.alloc(1024)
buffer.writeUint8(0x03, 0)
buffer.writeUint8(0x05, 1)
buffer.writeUint8(screenIndex, 2) // TODO - index
const buffer = new Uint8Array(1024)
buffer[0] = 0x03
buffer[1] = 0x05
buffer[2] = screenIndex // TODO - index
ps.push(this.#device.sendReports([buffer]))
}
// TODO - clear rgb?
Expand Down
43 changes: 25 additions & 18 deletions packages/core/src/services/encoderLed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ export class EncoderLedService {

if (!control.hasLed) throw new Error('Encoder does not have an LED')

const buffer = Buffer.alloc(1024)
buffer.writeUInt8(0x02, 0)
buffer.writeUInt8(0x10, 1)
buffer.writeUInt8(encoder, 2)
buffer.writeUInt8(red, 3)
buffer.writeUInt8(green, 4)
buffer.writeUInt8(blue, 5)
const buffer = new Uint8Array(1024)
buffer[0] = 0x02
buffer[1] = 0x10
buffer[2] = encoder
buffer[3] = red
buffer[4] = green
buffer[5] = blue
await this.#device.sendReports([buffer])
}

Expand All @@ -51,28 +51,35 @@ export class EncoderLedService {

if (control.ledRingSteps <= 0) throw new Error('Encoder does not have an LED ring')

const buffer = Buffer.alloc(1024)
buffer.fill(Buffer.from([red, green, blue]), 3, 3 + control.ledRingSteps * 3)
buffer.writeUint8(0x02, 0)
buffer.writeUint8(0x0f, 1)
buffer.writeUint8(encoder, 2)
const buffer = new Uint8Array(1024)
buffer[0] = 0x02
buffer[1] = 0x0f
buffer[2] = encoder
for (let i = 0; i < control.ledRingSteps; i++) {
const offset = 3 + i * 3
buffer[offset] = red
buffer[offset + 1] = green
buffer[offset + 2] = blue
}

await this.#device.sendReports([buffer])
}

public async setEncoderRingColors(encoder: EncoderIndex, colors: number[] | Buffer | Uint8Array): Promise<void> {
public async setEncoderRingColors(encoder: EncoderIndex, colors: number[] | Uint8Array): Promise<void> {
const control = this.#encoderControls.find((c) => c.index === encoder)
if (!control) throw new Error(`Invalid encoder index ${encoder}`)

if (control.ledRingSteps <= 0) throw new Error('Encoder does not have an LED ring')

if (colors.length !== control.ledRingSteps * 3) throw new Error('Invalid colors length')

const buffer = Buffer.alloc(1024)
Buffer.from(colors).copy(buffer, 3, 0, control.ledRingSteps * 3)
buffer.writeUint8(0x02, 0)
buffer.writeUint8(0x0f, 1)
buffer.writeUint8(encoder, 2)
const colorsBuffer = colors instanceof Uint8Array ? colors : new Uint8Array(colors)

const buffer = new Uint8Array(1024)
buffer[0] = 0x02
buffer[1] = 0x0f
buffer[2] = encoder
buffer.set(colorsBuffer, 3)

await this.#device.sendReports([buffer])
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export interface StreamDeck extends EventEmitter<StreamDeckEvents> {
* @param index The encoder to fill
* @param colors rgb packed pixel values for the encoder ring
*/
setEncoderRingColors(index: EncoderIndex, colors: number[] | Buffer | Uint8Array): Promise<void>
setEncoderRingColors(index: EncoderIndex, colors: number[] | Uint8Array): Promise<void>

/**
* Fill a region of the lcd segment, ignoring the boundaries of the encoders
Expand Down
23 changes: 13 additions & 10 deletions packages/tcp/src/device2Info.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import type { StreamDeckTcpChildDeviceInfo } from '@elgato-stream-deck/core'
import { uint8ArrayToDataView, type StreamDeckTcpChildDeviceInfo } from '@elgato-stream-deck/core'

export function parseDevice2Info(device2Info: Buffer): Omit<StreamDeckTcpChildDeviceInfo, 'model'> | null {
if (device2Info.readUInt8(4) !== 0x02) {
export function parseDevice2Info(device2Info: Uint8Array): Omit<StreamDeckTcpChildDeviceInfo, 'model'> | null {
if (device2Info[4] !== 0x02) {
// Nothing connected, or not OK
return null
}

const vendorId = device2Info.readUInt16LE(26)
const productId = device2Info.readUInt16LE(28)
const dataView = uint8ArrayToDataView(device2Info)

const vendorId = dataView.getUint16(26, true)
const productId = dataView.getUint16(28, true)

const serialNumberStart = 94
const serialNumberEnd = 125
const firstNullInSerial = device2Info.subarray(serialNumberStart, serialNumberEnd).indexOf(0x00)
const serialNumber = device2Info.toString(
'ascii',
serialNumberStart,
firstNullInSerial > -1 ? serialNumberStart + firstNullInSerial : serialNumberEnd,
const serialNumber = new TextDecoder('ascii').decode(
device2Info.subarray(
serialNumberStart,
firstNullInSerial > -1 ? serialNumberStart + firstNullInSerial : serialNumberEnd,
),
)

const tcpPort = device2Info.readUInt16LE(126)
const tcpPort = dataView.getUint16(126, true)

return {
serialNumber,
Expand Down
28 changes: 15 additions & 13 deletions packages/tcp/src/hid-device.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as EventEmitter from 'events'
import type {
HIDDeviceInfo,
HIDDevice,
HIDDeviceEvents,
ChildHIDDeviceInfo,
StreamDeckTcpChildDeviceInfo,
import {
type HIDDeviceInfo,
type HIDDevice,
type HIDDeviceEvents,
type ChildHIDDeviceInfo,
type StreamDeckTcpChildDeviceInfo,
uint8ArrayToDataView,
} from '@elgato-stream-deck/core'
import type { SocketWrapper } from './socketWrapper.js'
import { parseDevice2Info } from './device2Info.js'
Expand Down Expand Up @@ -98,23 +99,23 @@ export class TcpHidDevice extends EventEmitter<HIDDeviceEvents> implements HIDDe
// await this.#socket.close()
}

async sendFeatureReport(data: Buffer): Promise<void> {
async sendFeatureReport(data: Uint8Array): Promise<void> {
// Ensure the buffer is 1024 bytes long
let dataFull = data
if (data.length != 1024) {
dataFull = Buffer.alloc(1024)
data.copy(dataFull, 0, 0, Math.min(data.length, dataFull.length))
dataFull = new Uint8Array(1024)
dataFull.set(data.slice(0, Math.min(data.length, dataFull.length)))
}

this.#socket.sendMessages([dataFull])
}

async getFeatureReport(reportId: number, _reportLength: number): Promise<Buffer> {
async getFeatureReport(reportId: number, _reportLength: number): Promise<Uint8Array> {
return this.#executeSingletonCommand(reportId, this.#isPrimary)
}

readonly #pendingSingletonCommands = new Map<number, QueuedCommand>()
async #executeSingletonCommand(commandType: number, isPrimary: boolean): Promise<Buffer> {
async #executeSingletonCommand(commandType: number, isPrimary: boolean): Promise<Uint8Array> {
// if (!this.connected) throw new Error('Not connected')

const existingCommand = this.#pendingSingletonCommands.get(commandType)
Expand Down Expand Up @@ -167,8 +168,9 @@ export class TcpHidDevice extends EventEmitter<HIDDeviceEvents> implements HIDDe
const devicePath = `tcp://${this.#socket.address}:${this.#socket.port}`

if (this.#isPrimary) {
const vendorId = deviceInfo.data.readUInt16LE(12)
const productId = deviceInfo.data.readUInt16LE(14)
const dataView = uint8ArrayToDataView(deviceInfo.data)
const vendorId = dataView.getUint16(12, true)
const productId = dataView.getUint16(14, true)

this.#loadedHidInfo = {
vendorId: vendorId,
Expand Down
2 changes: 1 addition & 1 deletion packages/tcp/src/socketWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export class SocketWrapper extends EventEmitter<SocketWrapperEvents> {
}
}

sendMessages(buffers: Buffer[]): void {
sendMessages(buffers: Uint8Array[]): void {
// TODO - await write?
for (const buffer of buffers) {
this.#socket.write(buffer)
Expand Down
2 changes: 1 addition & 1 deletion packages/tcp/src/tcpWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ export class StreamDeckTcpWrapper extends StreamDeckProxy implements StreamDeckT

const data = await this.#device.getFeatureReport(0x85, -1)

return data.toString('hex', 4, 10) // TODO - add colons
return new TextDecoder('hex').decode(data.subarray(4, 10)) // TODO - add colons
}
}

0 comments on commit d27f1c8

Please sign in to comment.