diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 6207baf9..fada7a69 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -87,6 +87,31 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + verify-udev-generation: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.x + - name: yarn install + run: | + corepack enable + yarn install + - name: generate files + run: | + yarn build:root + yarn build:udev + - name: verify files are up to date + uses: infotroph/tree-is-clean@v1.0.6 + with: + check_untracked: false + publish-demo: name: Publish demo to Github Pages runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 6a1d9b82..8bdbbe86 100644 --- a/.gitignore +++ b/.gitignore @@ -75,4 +75,5 @@ typings/ !.yarn/sdks !.yarn/versions -hack-* \ No newline at end of file +hack-* +udev-generator-rules.json diff --git a/package.json b/package.json index 03a5e423..063ef74e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "run build:root --watch", "dev:demo": "yarn workspace @elgato-stream-deck/webhid-demo start", "build": "run build:root && yarn workspace @elgato-stream-deck/webhid-demo build", - "build:root": "(run g:rimraf packages/*/dist || true) && run g:tsc --build tsconfig.build.json", + "build:root": "(run g:rimraf packages/*/dist || true) && run g:tsc --build tsconfig.build.json && run build:udev", + "build:udev": "node ./scripts/regenerate-udev.mjs", "test": "run lint && run unit", "unit": "jest", "lint:raw": "eslint", @@ -52,6 +53,7 @@ "ts-node": "^10.9.2", "typescript": "~5.5.4", "typescript-eslint": "^8.32.1", + "udev-generator": "^1.0.1", "usb": "^2.15.0" }, "workspaces": [ diff --git a/packages/node/package.json b/packages/node/package.json index c3b802f2..c6ae35cf 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -46,7 +46,8 @@ ], "files": [ "dist", - "udev" + "udev", + "udev-generator-rules.json" ], "engines": { "node": ">=18.18" diff --git a/packages/node/udev/50-elgato-stream-deck-headless.rules b/packages/node/udev/50-elgato-stream-deck-headless.rules index 1ed807bf..cd6be97a 100644 --- a/packages/node/udev/50-elgato-stream-deck-headless.rules +++ b/packages/node/udev/50-elgato-stream-deck-headless.rules @@ -1,18 +1,5 @@ SUBSYSTEM=="input", GROUP="input", MODE="0660" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00aa", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b8", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b9", MODE:="660", GROUP="plugdev" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00ba", MODE:="660", GROUP="plugdev" + KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE:="660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE:="660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE:="660", GROUP="plugdev" @@ -20,6 +7,7 @@ KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE:="660 KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE:="660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE:="660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE:="660", GROUP="plugdev" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="008f", MODE:="660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE:="660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE:="660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE:="660", GROUP="plugdev" diff --git a/packages/node/udev/50-elgato-stream-deck-user.rules b/packages/node/udev/50-elgato-stream-deck-user.rules index d6c3d064..b8c37f24 100644 --- a/packages/node/udev/50-elgato-stream-deck-user.rules +++ b/packages/node/udev/50-elgato-stream-deck-user.rules @@ -1,29 +1,17 @@ -SUBSYSTEM=="input", GROUP="input", MODE="0660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00aa", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b8", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b9", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00ba", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00aa", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b8", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b9", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00ba", MODE="660", TAG+="uaccess" +SUBSYSTEM=="input", GROUP="input", MODE="0660" + +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="008f", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00aa", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b8", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b9", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00ba", MODE:="660", TAG+="uaccess" diff --git a/packages/webhid/package.json b/packages/webhid/package.json index e20d2d7b..664199fb 100644 --- a/packages/webhid/package.json +++ b/packages/webhid/package.json @@ -30,7 +30,8 @@ ], "files": [ "dist", - "udev" + "udev", + "udev-generator-rules.json" ], "dependencies": { "@elgato-stream-deck/core": "7.3.0", diff --git a/packages/webhid/udev/50-elgato-stream-deck-user.rules b/packages/webhid/udev/50-elgato-stream-deck-user.rules index d6c3d064..b8c37f24 100644 --- a/packages/webhid/udev/50-elgato-stream-deck-user.rules +++ b/packages/webhid/udev/50-elgato-stream-deck-user.rules @@ -1,29 +1,17 @@ -SUBSYSTEM=="input", GROUP="input", MODE="0660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00aa", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b8", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b9", MODE="660", TAG+="uaccess" -SUBSYSTEM=="usb", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00ba", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00aa", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b8", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b9", MODE="660", TAG+="uaccess" -KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00ba", MODE="660", TAG+="uaccess" +SUBSYSTEM=="input", GROUP="input", MODE="0660" + +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0060", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0063", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006c", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="006d", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0080", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0084", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0086", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="008f", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="0090", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="009a", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00a5", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00aa", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b8", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00b9", MODE:="660", TAG+="uaccess" +KERNEL=="hidraw*", ATTRS{idVendor}=="0fd9", ATTRS{idProduct}=="00ba", MODE:="660", TAG+="uaccess" diff --git a/scripts/regenerate-udev.mjs b/scripts/regenerate-udev.mjs new file mode 100644 index 00000000..8dffb588 --- /dev/null +++ b/scripts/regenerate-udev.mjs @@ -0,0 +1,47 @@ +/* eslint-disable n/no-extraneous-import */ +// @ts-check + +import fs from 'node:fs/promises' + +import { UdevRuleGenerator } from 'udev-generator' +import { DEVICE_MODELS2, VENDOR_ID } from '@elgato-stream-deck/core' + +const generator = new UdevRuleGenerator() + +for (const model of Object.values(DEVICE_MODELS2)) { + for (const productId of model.productIds) { + if (productId >= 0xfff0) continue // Skip reserved product IDs + generator.addDevice(VENDOR_ID, productId) + } +} + +const headlessStr = generator.generateFile({ + mode: 'headless', +}) +const desktopStr = generator.generateFile({ + mode: 'desktop', +}) + +const rulesJSON = JSON.stringify(generator.rules, undefined, '\t') + +await fs.writeFile( + new URL('../packages/node/udev/50-elgato-stream-deck-user.rules', import.meta.url), + desktopStr, + 'utf8', +) +await fs.writeFile( + new URL('../packages/node/udev/50-elgato-stream-deck-headless.rules', import.meta.url), + headlessStr, + 'utf8', +) + +await fs.writeFile( + new URL('../packages/webhid/udev/50-elgato-stream-deck-user.rules', import.meta.url), + desktopStr, + 'utf8', +) + +await fs.writeFile(new URL('../packages/node/udev-generator-rules.json', import.meta.url), rulesJSON, 'utf8') +await fs.writeFile(new URL('../packages/webhid/udev-generator-rules.json', import.meta.url), rulesJSON, 'utf8') + +console.log('Udev rules regenerated successfully!') diff --git a/yarn.lock b/yarn.lock index 5c9a3288..3bb8c62c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5137,6 +5137,7 @@ __metadata: ts-node: "npm:^10.9.2" typescript: "npm:~5.5.4" typescript-eslint: "npm:^8.32.1" + udev-generator: "npm:^1.0.1" usb: "npm:^2.15.0" languageName: unknown linkType: soft @@ -12035,6 +12036,13 @@ __metadata: languageName: node linkType: hard +"udev-generator@npm:^1.0.1": + version: 1.0.1 + resolution: "udev-generator@npm:1.0.1" + checksum: 10c0/e1703e73c7aeddaddcdfe219039a4d3979a0adaaae318ec272b4b404b4084b19de67fdd05a7c4fba2fad51ebc5cbb6619b07f668b31eec41ce8a752be7a1ec9d + languageName: node + linkType: hard + "uglify-js@npm:^3.1.4": version: 3.17.4 resolution: "uglify-js@npm:3.17.4"