跳到主要内容
版本:Matrix OS 4.0 🚧

System Remote API

The System Remote API is the Matrix OS protocol for device information and system-level control from host software. Use it to read device/app metadata, reboot or open settings, update system LED state, and enter a target app such as Developer APP.

Matrix OS 4.0 beta

The System Remote API is a beta developer protocol. Command IDs are documented so tool authors can build against the current firmware, but details may still change before the stable release.

When To Use It

Use System Remote API when the host needs to control Matrix OS itself:

  • identify the device and firmware version
  • read the active app metadata
  • enter or quit apps
  • open settings, reboot, or bootloader
  • enter Developer APP before using the device as a clean controller canvas

Do not use System Remote API for high-frequency controller input or mirrored LED frames. Once Developer APP is active, use the Developer APP protocol for live input reports and LED canvas writes.

HID Transport

WebHID clients use the Matrix OS HID collection:

  • vendor ID: 0x0203
  • usage page: 0xFF00
  • usage: 0x01
  • System Remote API report ID: 0xCB
  • report payload size: up to 63 bytes

HID request layout:

byte 0 command
byte 1..62 command payload

HID reply layout:

byte 0 command | 0x80
byte 1.. reply data, if the command returns data

System Remote API HID replies do not include a status byte. This is different from Developer APP, where HID replies are command | 0x80, then status, then optional data.

Commands that only ACK return the reply command byte by itself. Commands documented as "no success reply" do not send a reply when they succeed, so host tools should not wait for one.

Malformed or unsupported System Remote API commands may not send a reply. Use a timeout and treat the request as invalid or unsupported.

WebHID sendReport(reportId, data) takes the report ID separately. Host libraries such as hidapi often expect the report ID as the first byte of the written buffer, so the same request may be written as 0xCB command payload... in those APIs.

SysEx Transport

System Remote API also supports Matrix OS system SysEx. This is a different framing from Developer APP SysEx.

Request framing:

F0 23 command payload... F7

Reply framing:

F0 24 reply... F7

System SysEx uses 7-bit payload bytes. In SysEx replies, the reply command is the command ID itself, not command | 0x80.

Encodings

The request payloads below start after the command byte.

TypeHID encodingSystem SysEx encoding
u8one byteone 7-bit byte
u16big-endian, msb lsbthree 7-bit bytes: bits15..14 bits13..7 bits6..0
u32big-endian, b31..24 b23..16 b15..8 b7..0five 7-bit bytes: bits31..25 bits24..18 bits17..11 bits10..4 bits3..0<<3
stringASCII-compatible, null-terminated7-bit ASCII-compatible, null-terminated
colorMatrix OS Color(uint32_t WRGB), sent as u32Matrix OS Color(uint32_t WRGB), sent as SysEx u32
layer0..MAX_LED_LAYERS - 1, or 255 for the current/default layer where supportedone 7-bit byte where supported

For color, 0x00FF0000 is red, 0x0000FF00 is green, 0x000000FF is blue, and the top byte is white.

For example, the System SysEx u32 value 0x12345678 is encoded as:

09 0D 0A 67 40

New browser and desktop tools should prefer HID unless they specifically need a MIDI-only path.

Enter Developer APP

Developer APP is hidden from the normal application launcher. A host enters it through System Remote API, then switches to the Developer APP report.

System report 0xCB:
send: 0x44 # OPEN_DEVELOPER_APP
expect: 0xC4 # ACK

Developer APP report 0xFF:
send: 0x00 # PING
expect: 0x80 0x00 ... # Developer APP ACK with status OK

Recommended host flow:

  1. Open the Matrix OS HID device.
  2. Send OPEN_DEVELOPER_APP (0x44) on report 0xCB.
  3. Wait for reply 0xC4.
  4. Wait about 250 ms for the app switch.
  5. Poll Developer APP PING (0x00) on report 0xFF until it replies.
  6. Use Developer APP for input events and LED canvas writes.

ENTER_APP_VIA_ID (0x18) can enter apps by app ID, but OPEN_DEVELOPER_APP is the direct command for this workflow.

App IDs And Discovery

System Remote API can report the active app, but it does not currently enumerate every installed app.

  • Use GET_APP_ID, GET_APP_NAME, GET_APP_AUTHOR, and GET_APP_VERSION to identify the app that is currently running.
  • Use OPEN_DEVELOPER_APP when the host wants Developer APP. It avoids hard-coding the Developer APP ID.
  • Use ENTER_APP_VIA_ID only when the host already knows the target app ID.

Native Matrix OS app IDs are generated from the app author and app name with the same ID used by Matrix OS when the app is registered. In the Matrix OS source tree, C++ code can use APPID(author, name) or MatrixOS::SYS::ExecuteAPP(author, appName). Host software should treat app IDs as firmware/app metadata, not as values discoverable from this protocol today.

After ENTER_APP_VIA_ID ACKs, app switching is asynchronous. Confirm success by waiting briefly, then reading GET_APP_ID or GET_APP_NAME, or by polling the target app's own protocol if it has one. For Developer APP, poll Developer APP PING on report 0xFF.

Command Summary

CommandIDRequest payloadSuccess reply
GET_OS_VERSION0x00none0x80 major minor patch build release
GET_DEVICE_NAME0x01none0x81 name\0
GET_DEVICE_MODEL_ID0x02none0x82 model\0
GET_DEVICE_SERIAL0x03none0x83 serial\0
GET_DEVICE_ID0x04none0x84 device_id:u16
GET_DEVICE_SIZE0x05none0x85 width:u8 height:u8
GET_DEVICE_LED_COUNT0x06none0x86 led_count:u32
SET_DEVICE_ID0x0Bdevice_id:u16no success reply
GET_APP_ID0x10none0x90 app_id:u32
GET_APP_NAME0x11none0x91 name\0
GET_APP_AUTHOR0x12none0x92 author\0
GET_APP_VERSION0x13none0x93 version:u32
ENTER_APP_VIA_ID0x18app_id:u320x98, then app switch
QUIT_APP0x1Fnone0x9F, then app quit
BOOTLOADER0x40none0xC0, then bootloader
REBOOT0x41none0xC1, then reboot
SLEEP0x42none0xC2
OPEN_SETTINGS0x43none0xC3, then settings opens
OPEN_DEVELOPER_APP0x44none0xC4, then Developer APP opens
LED_SET_COLOR_XY0x50x:u16 y:u16 color:u32 [layer:u8]no success reply
LED_SET_COLOR_ID0x51id:u16 color:u32 [layer:u8]no success reply
LED_FILL0x52color:u32 [layer:u8]no success reply
LED_FILL_PARTITION0x53partition:u8 color:u32 [layer:u8]no success reply
LED_UPDATE0x54[layer:u8]no success reply
LED_CREATELAYER0x55[crossfade_ms:u16]0xD5 layer:u8
LED_COPYLAYER0x56from_layer:u8 to_layer:u8no success reply
LED_DESTROYLAYER0x57[crossfade_ms:u16]0xD7 destroyed:u8
LED_SET_BRIGHTNESS0x59brightness:u8no success reply
LED_FADE0x5A[crossfade_ms:u16]no success reply
GET_LED_CURRENT_LAYER0x5Cnone0xDC layer:u8
GET_LED_BRIGHTNESS0x5Dnone0xDD brightness:u8
KEYPAD_GET_KEY_XY0x60x:u8 y:u80xE0 found:u8 [cluster_id:u8 member_id:u16]
KEYPAD_GET_KEY_ID0x61cluster_id:u8 member_id:u160xE1 found:u8 [key info...]

FACTORY_RESET (0x4F) is reserved and unsupported in the current public protocol.

Device And App Metadata

GET_OS_VERSION (0x00)

Request payload: none.

Reply:

0x80 major minor patch build release

String Metadata

These commands return null-terminated strings:

CommandIDReply
GET_DEVICE_NAME0x010x81 name\0
GET_DEVICE_MODEL_ID0x020x82 model\0
GET_DEVICE_SERIAL0x030x83 serial\0
GET_APP_NAME0x110x91 name\0
GET_APP_AUTHOR0x120x92 author\0

Numeric Metadata

CommandIDReply data
GET_DEVICE_ID0x04device_id:u16
GET_DEVICE_SIZE0x05width:u8 height:u8
GET_DEVICE_LED_COUNT0x06led_count:u32
GET_APP_ID0x10app_id:u32
GET_APP_VERSION0x13version:u32
GET_LED_CURRENT_LAYER0x5Clayer:u8
GET_LED_BRIGHTNESS0x5Dbrightness:u8

Each reply starts with command | 0x80, then the data shown in the table.

SET_DEVICE_ID (0x0B)

Request payload:

device_id:u16

Success reply: none.

App And System Actions

Action commands ACK first, wait briefly inside Matrix OS, then perform the action.

CommandIDPayloadACK
ENTER_APP_VIA_ID0x18app_id:u320x98
QUIT_APP0x1Fnone0x9F
BOOTLOADER0x40none0xC0
REBOOT0x41none0xC1
SLEEP0x42none0xC2
OPEN_SETTINGS0x43none0xC3
OPEN_DEVELOPER_APP0x44none0xC4

Use OPEN_DEVELOPER_APP instead of ENTER_APP_VIA_ID when the host specifically wants Developer APP.

System LED Commands

System Remote API LED commands operate on Matrix OS LED layers. They are useful for simple system-level drawing, not for high-frame-rate host mirroring. For high-frequency LED canvas updates, enter Developer APP and use its LED commands.

WebHID padding and optional fields

Some Matrix OS HID stacks receive fixed-size reports. When a command has an optional layer or crossfade_ms, a zero-padded WebHID report can be interpreted as an explicit value. If you need a specific layer, include it explicitly. Use 255 for the current/default layer where supported.

LED_SET_COLOR_XY (0x50)

Request payload:

x:u16 y:u16 color:u32 [layer:u8]

Sets one LED by coordinate. Success reply: none.

LED_SET_COLOR_ID (0x51)

Request payload:

id:u16 color:u32 [layer:u8]

Sets one LED by LED index. Success reply: none.

LED_FILL (0x52)

Request payload:

color:u32 [layer:u8]

Fills the current LED target. Success reply: none.

LED_FILL_PARTITION (0x53)

Request payload:

partition:u8 color:u32 [layer:u8]

Fills a numeric LED partition. Success reply: none.

LED_UPDATE (0x54)

Request payload:

[layer:u8]

Pushes LED output for a layer. Success reply: none.

Layer Commands

CommandIDPayloadReply
LED_CREATELAYER0x55[crossfade_ms:u16]0xD5 layer:u8
LED_COPYLAYER0x56from_layer:u8 to_layer:u8no success reply
LED_DESTROYLAYER0x57[crossfade_ms:u16]0xD7 destroyed:u8
LED_FADE0x5A[crossfade_ms:u16]no success reply

LED_SET_BRIGHTNESS (0x59)

Request payload:

brightness:u8

Success reply: none.

Input Read Commands

These wire commands keep the KEYPAD names, but they read Matrix OS Input state.

KEYPAD_GET_KEY_XY (0x60)

Request payload:

x:u8 y:u8

Reply:

0xE0 found:u8 [cluster_id:u8 member_id:u16]

If found is 0, no additional key data follows.

KEYPAD_GET_KEY_ID (0x61)

Request payload:

cluster_id:u8 member_id:u16

Reply:

0xE1 found:u8 [cluster_id:u8 member_id:u16 x:u16 y:u16 state:u8 pressure:u16 velocity:u16]

If found is 0, no additional key data follows. state uses the same keypad state values documented in Developer APP.

Minimal WebHID Helpers

const VENDOR_ID = 0x0203
const USAGE_PAGE = 0xff00
const USAGE = 0x01
const SYSTEM_REPORT_ID = 0xcb

function makeReport(...bytes) {
const report = new Uint8Array(63)
report.set(bytes)
return report
}

function waitForReport(device, reportId, match, timeoutMs = 800) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
cleanup()
reject(new Error("Timed out waiting for System Remote API reply"))
}, timeoutMs)

function cleanup() {
clearTimeout(timer)
device.removeEventListener("inputreport", onInputReport)
}

function onInputReport(event) {
if (event.reportId !== reportId) return
const data = new Uint8Array(event.data.buffer, event.data.byteOffset, event.data.byteLength)
if (!match(data)) return
cleanup()
resolve(data)
}

device.addEventListener("inputreport", onInputReport)
})
}

async function requestSystemCommand(device, command, payload = [], timeoutMs = 800) {
const reply = waitForReport(
device,
SYSTEM_REPORT_ID,
(data) => data[0] === (command | 0x80),
timeoutMs,
)
await device.sendReport(SYSTEM_REPORT_ID, makeReport(command, ...payload))
return reply
}

const [device] = await navigator.hid.requestDevice({
filters: [{ vendorId: VENDOR_ID, usagePage: USAGE_PAGE, usage: USAGE }],
})

await device.open()

const version = await requestSystemCommand(device, 0x00)
console.log("Matrix OS version", version.slice(1, 6))

await requestSystemCommand(device, 0x44)
console.log("Developer APP requested")

After 0x44 ACKs, switch to report 0xFF and use Developer APP PING to verify the app is ready.

Comments