Skip to content

Commit

Permalink
feat: initial VEC Footpedal support (#2944)
Browse files Browse the repository at this point in the history
Co-authored-by: Julian Waller <git@julusian.co.uk>
  • Loading branch information
peternewman and Julusian authored Jul 9, 2024
1 parent 7cf0e6f commit daad9b5
Show file tree
Hide file tree
Showing 15 changed files with 264 additions and 2 deletions.
4 changes: 4 additions & 0 deletions assets/linux/50-companion-desktop.rules
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0030", MODE:="666"
KERNEL=="hidraw*", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0010", MODE:="666", TAG+="uaccess"
KERNEL=="hidraw*", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0020", MODE:="666", TAG+="uaccess"
KERNEL=="hidraw*", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0030", MODE:="666", TAG+="uaccess"

# vec footpedal
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", ATTRS{idProduct}=="00ff", MODE:="666", TAG+="uaccess"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", ATTRS{idProduct}=="00ff", MODE:="666", TAG+="uaccess"
4 changes: 4 additions & 0 deletions assets/linux/50-companion-headless.rules
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0030", MODE:="666"
KERNEL=="hidraw*", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0010", MODE:="666", GROUP="companion"
KERNEL=="hidraw*", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0020", MODE:="666", GROUP="companion"
KERNEL=="hidraw*", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0030", MODE:="666", GROUP="companion"

# vec footpedal
SUBSYSTEM=="usb", ATTRS{idVendor}=="05f3", ATTRS{idProduct}=="00ff", MODE:="666", GROUP="companion"
KERNEL=="hidraw*", ATTRS{idVendor}=="05f3", ATTRS{idProduct}=="00ff", MODE:="666", GROUP="companion"
1 change: 1 addition & 0 deletions companion/lib/Data/UserConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class DataUserConfig extends CoreBase {
usb_hotplug: true,
loupedeck_enable: false,
contour_shuttle_enable: false,
vec_footpedal_enable: false,

pin_enable: false,
link_lockouts: false,
Expand Down
18 changes: 18 additions & 0 deletions companion/lib/Surface/Controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { getStreamDeckDeviceInfo } from '@elgato-stream-deck/node'
import { usb } from 'usb'
// @ts-ignore
import shuttleControlUSB from 'shuttle-control-usb'
// @ts-ignore
import vecFootpedal from 'vec-footpedal'
import { listLoupedecks, LoupedeckModelId } from '@loupedeck/node'
import SurfaceHandler, { getSurfaceName } from './Handler.js'
import SurfaceIPElgatoEmulator, { EmulatorRoom } from './IP/ElgatoEmulator.js'
Expand All @@ -37,6 +39,7 @@ import XKeysDriver from './USB/XKeys.js'
import LoupedeckLiveDriver from './USB/LoupedeckLive.js'
import SurfaceUSBLoupedeckCt from './USB/LoupedeckCt.js'
import ContourShuttleDriver from './USB/ContourShuttle.js'
import VECFootpedalDriver from './USB/VECFootpedal.js'
import SurfaceIPVideohubPanel from './IP/VideohubPanel.js'
import FrameworkMacropadDriver from './USB/FrameworkMacropad.js'
import CoreBase from '../Core/Base.js'
Expand Down Expand Up @@ -891,6 +894,21 @@ class SurfaceController extends CoreBase {
'infinitton',
InfinittonDriver
)
} else if (
// More specific match has to be above xkeys
deviceInfo.vendorId === vecFootpedal.vids.VEC &&
deviceInfo.productId === vecFootpedal.pids.FOOTPEDAL
) {
if (this.userconfig.getKey('vec_footpedal_enable')) {
await this.#addDevice(
{
path: deviceInfo.path,
options: {},
},
'vec-footpedal',
VECFootpedalDriver
)
}
} else if (deviceInfo.vendorId === 1523 && deviceInfo.interface === 0) {
if (this.userconfig.getKey('xkeys_enable')) {
await this.#addDevice(
Expand Down
169 changes: 169 additions & 0 deletions companion/lib/Surface/USB/VECFootpedal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// @ts-nocheck
/*
* This file is part of the Companion project
* Copyright (c) 2024 Peter Newman
* Author: Peter Newman
*
* This program is free software.
* You should have received a copy of the MIT licence as well as the Bitfocus
* Individual Contributor License Agreement for companion along with
* this program.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the Companion software without
* disclosing the source code of your own applications.
*
*/

import EventEmitter from 'events'
import vecFootpedal from 'vec-footpedal'
import LogController from '../../Log/Controller.js'

const vecFootpedalInfo = {
// Treat as:
// 3 buttons
totalCols: 3,
totalRows: 1,

buttons: [
[0, 0],
[1, 0],
[2, 0],
],
}

function buttonToXy(modelInfo, info) {
return modelInfo.buttons[info - 1]
}

class SurfaceUSBVECFootpedal extends EventEmitter {
/**
* @type {import('winston').Logger}
* @access private
* @readonly
*/
#logger

constructor(devicePath, contourShuttle, modelInfo, deviceInfo) {
super()

this.#logger = LogController.createLogger(`Surface/USB/VECFootpedal/${devicePath}`)

this.vecFootpedal = vecFootpedal
this.deviceInfo = deviceInfo
this.modelInfo = modelInfo

this.config = {
// No config currently present
}

this.#logger.debug(`Adding VEC Footpedal USB device ${devicePath}`)

this.info = {
type: `VEC Footpedal ${this.deviceInfo.name}`,
devicePath: devicePath,
configFields: [],
deviceId: `vecfootpedal:${this.deviceInfo.id}`,
}

this.gridSize = {
columns: this.modelInfo.totalCols,
rows: this.modelInfo.totalRows,
}

this.vecFootpedal.on('error', (error) => {
console.error(error)
this.emit('remove')
})

this.vecFootpedal.on('buttondown', (info) => {
const xy = buttonToXy(this.modelInfo, info)
if (xy === undefined) {
return
}

this.emit('click', ...xy, true)
})

this.vecFootpedal.on('buttonup', (info) => {
const xy = buttonToXy(this.modelInfo, info)
if (xy === undefined) {
return
}

this.emit('click', ...xy, false)
})

this.vecFootpedal.on('disconnect', (error) => {
console.error(error)
this.emit('remove')
})
}

async #init() {
this.#logger.debug(`VEC Footpedal ${this.deviceInfo.name} detected`)
}

/**
* Open a VEC Footpedal
* @param {string} devicePath
* @returns {Promise<SurfaceUSBVECFootpedal>}
*/
static async create(devicePath) {
const pedal = vecFootpedal
// We're doing device search via Companion so don't run it here too
pedal.start(false)
try {
let deviceInfo = null
let info = null
pedal.connect(devicePath)
deviceInfo = pedal.getDeviceByPath(devicePath)
switch (deviceInfo.name) {
case 'VEC Footpedal':
info = vecFootpedalInfo
break
default:
throw new Error(`Unknown VEC Footpedal device detected: ${deviceInfo.name}`)
}
if (!info) {
throw new Error('Unsupported model')
}

const self = new SurfaceUSBVECFootpedal(devicePath, pedal, info, deviceInfo)

await self.#init()

return self
} catch (e) {
pedal.stop()

throw e
}
}

/**
* Process the information from the GUI and what is saved in database
* @param {Record<string, any>} config
* @param {boolean=} _force
* @returns false when nothing happens
*/
setConfig(config, _force) {
// No config currently present
this.config = config
}

quit() {
this.vecFootpedal.close()
}

draw() {
// Should never be fired
}

clearDeck() {
// Not relevant for this device
}
}

export default SurfaceUSBVECFootpedal
1 change: 1 addition & 0 deletions companion/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"usb": "2.13.0",
"utf-8-validate": "^6.0.4",
"uuid": "^10.0.0",
"vec-footpedal": "^2.0.1",
"videohub-server": "^0.3.0",
"winston": "^3.13.0",
"workerpool": "^9.1.3",
Expand Down
3 changes: 3 additions & 0 deletions docs/3_config/settings/surfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ More details on supported surfaces are available in the chapter on [Surfaces](#7

- **Enable connected Contour Shuttle (Requires Companion restart)**
Whether to enable support for connecting to Contour Shuttle devices.

- **Enable connected VEC Footpedal (Requires Companion restart)**
Whether to enable support for connecting to VEC Footpedal devices.
5 changes: 5 additions & 0 deletions docs/7_surfaces/vec_footpedal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
It is possible to use the VEC Footpedal with Companion since v3.4.0.

Enable support for it in the Companion settings, and rescan for USB devices.

The layout pretty closely matches what you would expect based on the device.
4 changes: 4 additions & 0 deletions docs/structure.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@
{
"label": "Contour Shuttle",
"file": "7_surfaces/contour_shuttle.md"
},
{
"label": "VEC Footpedal",
"file": "7_surfaces/vec_footpedal.md"
}
]
}
Expand Down
1 change: 1 addition & 0 deletions shared-lib/lib/Model/UserConfigModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface UserConfigModel {
usb_hotplug: boolean
loupedeck_enable: boolean
contour_shuttle_enable: boolean
vec_footpedal_enable: boolean

pin_enable: boolean
link_lockouts: boolean
Expand Down
21 changes: 21 additions & 0 deletions webui/src/UserConfig/SurfacesConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,27 @@ export const SurfacesConfig = observer(function SurfacesConfig({ config, setValu
</CButton>
</td>
</tr>
<tr>
<td>
Enable connected VEC Footpedal
<br />
<em>(Requires Companion restart)</em>
</td>
<td>
<CFormSwitch
className="float-right"
color="success"
checked={config.vec_footpedal_enable}
size="xl"
onChange={(e) => setValue('vec_footpedal_enable', e.currentTarget.checked)}
/>
</td>
<td>
<CButton onClick={() => resetValue('vec_footpedal_enable')} title="Reset to default">
<FontAwesomeIcon icon={faUndo} />
</CButton>
</td>
</tr>
</>
)
})
12 changes: 12 additions & 0 deletions webui/src/Wizard/ApplyStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ export function ApplyStep({ oldConfig, newConfig }: ApplyStepProps) {
)
}

if (oldConfig.setup_wizard === 0 || oldConfig.vec_footpedal_enable !== newConfig.vec_footpedal_enable) {
changes.push(
newConfig.vec_footpedal_enable ? (
<li>VEC Footpedal hardware will be detected by Companion.</li>
) : (
<li>
VEC Footpedal hardware will {oldConfig.setup_wizard > 0 ? 'no longer' : 'not'} be detected by Companion.
</li>
)
)
}

if (oldConfig.setup_wizard === 0 || oldConfig.videohub_panel_enabled !== newConfig.videohub_panel_enabled) {
changes.push(
newConfig.videohub_panel_enabled ? (
Expand Down
4 changes: 2 additions & 2 deletions webui/src/Wizard/FinishStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export function FinishStep({ oldConfig, newConfig }: FinishStepProps) {
{((newConfig.elgato_plugin_enable && oldConfig.elgato_plugin_enable !== newConfig.elgato_plugin_enable) ||
(!newConfig.xkeys_enable && oldConfig.xkeys_enable !== newConfig.xkeys_enable) ||
(!newConfig.loupedeck_enable && oldConfig.loupedeck_enable !== newConfig.loupedeck_enable) ||
(!newConfig.contour_shuttle_enable &&
oldConfig.contour_shuttle_enable !== newConfig.contour_shuttle_enable)) && (
(!newConfig.contour_shuttle_enable && oldConfig.contour_shuttle_enable !== newConfig.contour_shuttle_enable) ||
(!newConfig.vec_footpedal_enable && oldConfig.vec_footpedal_enable !== newConfig.vec_footpedal_enable)) && (
<CAlert color="danger">
After completing this wizard a restart of Companion is required to apply your USB detection settings. You will
need to do this manually.
Expand Down
8 changes: 8 additions & 0 deletions webui/src/Wizard/SurfacesStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export function SurfacesStep({ config, setValue }: SurfacesStepProps) {
/>
</div>

<div className="indent3">
<CFormCheck
label="VEC Footpedal USB Devices"
checked={config.vec_footpedal_enable}
onChange={(e) => setValue('vec_footpedal_enable', e.currentTarget.checked)}
/>
</div>

<br />
<h5>IP Surface Listeners</h5>
<div className="indent3">
Expand Down
11 changes: 11 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6755,6 +6755,7 @@ asn1@evs-broadcast/node-asn1:
usb: "npm:2.13.0"
utf-8-validate: "npm:^6.0.4"
uuid: "npm:^10.0.0"
vec-footpedal: "npm:^2.0.1"
videohub-server: "npm:^0.3.0"
webpack: "npm:^5.92.1"
webpack-cli: "npm:^5.1.4"
Expand Down Expand Up @@ -15163,6 +15164,16 @@ asn1@evs-broadcast/node-asn1:
languageName: node
linkType: hard

"vec-footpedal@npm:^2.0.1":
version: 2.0.1
resolution: "vec-footpedal@npm:2.0.1"
dependencies:
node-hid: "npm:^3.1.0"
usb: "npm:^2.9.0"
checksum: 10c0/2e158005d8ffc8547a929e9d9ab4ce2f1762d19df9c89037f701ec65d4915e1fc0543f16fd611c11de77298ddd81ff485484e34c68c8dd6fca9b49c6e7842d22
languageName: node
linkType: hard

"verror@npm:^1.10.0":
version: 1.10.1
resolution: "verror@npm:1.10.1"
Expand Down

0 comments on commit daad9b5

Please sign in to comment.