Skip to content

Commit

Permalink
feat: config to enable/disable different surface types
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian committed Feb 2, 2025
1 parent 5ddbbd1 commit 2752c4c
Show file tree
Hide file tree
Showing 21 changed files with 613 additions and 28 deletions.
91 changes: 85 additions & 6 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,71 @@ paths:
schema:
$ref: '#/components/schemas/ErrorResponse'

/surfaces/plugins/installed:
get:
summary: Get installed surface plugins
operationId: getSurfacePluginsInstalled
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/SurfacePluginInfo'
'400':
description: failed operation
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'

/surfaces/plugins/enabled:
get:
summary: Get enabled status of surface plugins
operationId: getSurfacePluginsEnabled
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: object
additionalProperties:
type: boolean
'400':
description: failed operation
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
post:
summary: Update enabled status of surface plugins
operationId: updateSurfacePluginsEnabled
requestBody:
content:
application/json:
schema:
type: object
additionalProperties:
type: boolean
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: object
additionalProperties:
type: boolean
'400':
description: failed operation
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'

/config:
get:
summary: Get current configuration
Expand Down Expand Up @@ -112,14 +177,12 @@ components:
type: boolean
description: Whether the client is connected to the Companion Satellite
companionVersion:
type:
- string
- null
type: string
nullable: true
description: Version of the Companion Satellite
companionApiVersion:
type:
- string
- null
type: string
nullable: true
description: API version of the Companion Satellite
required:
- connected
Expand Down Expand Up @@ -198,3 +261,19 @@ components:
- pluginId
- pluginName
# - isOpen

SurfacePluginInfo:
type: object
properties:
pluginId:
type: string
description: Plugin ID of the surface
pluginName:
type: string
description: Plugin name of the surface
pluginComment:
type: string
description: Comment about the plugin
required:
- pluginId
- pluginName
20 changes: 18 additions & 2 deletions satellite/src/apiTypes.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import Conf from 'conf'
import { CompanionSatelliteClient } from './client.js'
import { SatelliteConfig } from './config.js'
import { SatelliteConfig, SatelliteConfigInstance } from './config.js'
import type { components as openapiComponents } from './generated/openapi.js'

export type ApiStatusResponse = openapiComponents['schemas']['StatusResponse']
export type ApiConfigData = openapiComponents['schemas']['ConfigData']
export type ApiConfigDataUpdate = openapiComponents['schemas']['ConfigDataUpdate']
export type ApiSurfaceInfo = openapiComponents['schemas']['SurfaceInfo']
export type ApiSurfacePluginInfo = openapiComponents['schemas']['SurfacePluginInfo']
export type ApiSurfacePluginsEnabled = Record<string, boolean>

export type ApiConfigDataUpdateElectron = ApiConfigDataUpdate & Pick<Partial<ApiConfigData>, 'httpEnabled' | 'httpPort'>

Expand All @@ -17,6 +19,9 @@ export interface SatelliteUiApi {
getStatus: () => Promise<ApiStatusResponse>
rescanSurfaces: () => Promise<void>
connectedSurfaces: () => Promise<ApiSurfaceInfo[]>
surfacePlugins: () => Promise<ApiSurfacePluginInfo[]>
surfacePluginsEnabled: () => Promise<ApiSurfacePluginsEnabled>
surfacePluginsEnabledUpdate: (newConfig: ApiSurfacePluginsEnabled) => Promise<ApiSurfacePluginsEnabled>
}

export function compileStatus(client: CompanionSatelliteClient): ApiStatusResponse {
Expand All @@ -41,7 +46,7 @@ export function compileConfig(appConfig: Conf<SatelliteConfig>): ApiConfigData {
}
}

export function updateConfig(appConfig: Conf<SatelliteConfig>, newConfig: ApiConfigDataUpdateElectron): void {
export function updateConfig(appConfig: SatelliteConfigInstance, newConfig: ApiConfigDataUpdateElectron): void {
if (newConfig.host !== undefined) appConfig.set('remoteIp', newConfig.host)
if (newConfig.port !== undefined) appConfig.set('remotePort', newConfig.port)

Expand All @@ -51,3 +56,14 @@ export function updateConfig(appConfig: Conf<SatelliteConfig>, newConfig: ApiCon
if (newConfig.mdnsEnabled !== undefined) appConfig.set('mdnsEnabled', newConfig.mdnsEnabled)
if (newConfig.installationName !== undefined) appConfig.set('installationName', newConfig.installationName)
}

export function updateSurfacePluginsEnabledConfig(
appConfig: SatelliteConfigInstance,
newConfig: ApiSurfacePluginsEnabled,
): void {
for (const [pluginId, enabled] of Object.entries(newConfig)) {
appConfig.set(`surfacePluginsEnabled.${pluginId}`, !!enabled)
}

console.log('Updated surfacePluginsEnabled:', appConfig.get('surfacePluginsEnabled'))
}
19 changes: 19 additions & 0 deletions satellite/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { customAlphabet } from 'nanoid'

const nanoidHex = customAlphabet('0123456789abcdef')

export type SatelliteConfigInstance = Conf<SatelliteConfig>

export interface SatelliteConfig {
remoteIp: string
remotePort: number
Expand All @@ -15,6 +17,8 @@ export interface SatelliteConfig {
restPort: number

mdnsEnabled: boolean

surfacePluginsEnabled: Record<string, boolean>
}

export const satelliteConfigSchema: Schema<SatelliteConfig> = {
Expand Down Expand Up @@ -54,6 +58,21 @@ export const satelliteConfigSchema: Schema<SatelliteConfig> = {
description: 'Enable mDNS announcement',
default: true,
},

surfacePluginsEnabled: {
type: 'object',
patternProperties: {
'': {
type: 'boolean',
},
},
description: 'Enabled Surface Plugins',
default: {
'elgato-streamdeck': true,
loupedeck: true,
infinitton: true,
},
},
}

export function ensureFieldsPopulated(store: Conf<SatelliteConfig>): void {
Expand Down
1 change: 1 addition & 0 deletions satellite/src/device-types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface SurfacePluginDetection<TInfo> extends EventEmitter<SurfacePlugi
export interface SurfacePlugin<TInfo> {
readonly pluginId: string
readonly pluginName: string
readonly pluginComment?: string

/**
* Some plugins are forced to use a builtin detection mechanism by their surfaces or inner library
Expand Down
3 changes: 3 additions & 0 deletions satellite/src/device-types/loupedeck-live-s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export class LoupedeckLiveSWrapper extends EventEmitter<WrappedSurfaceEvents> im

async close(): Promise<void> {
this.#queue?.abort()

await this.#deck.blankDevice(true, true).catch(() => null)

await this.#deck.close()
}
async initDevice(client: CompanionClient, status: string): Promise<void> {
Expand Down
3 changes: 3 additions & 0 deletions satellite/src/device-types/loupedeck-live.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ export class LoupedeckLiveWrapper extends EventEmitter<WrappedSurfaceEvents> imp

async close(): Promise<void> {
this.#queue?.abort()

await this.#deck.blankDevice(true, true).catch(() => null)

await this.#deck.close()
}
async initDevice(client: CompanionClient, status: string): Promise<void> {
Expand Down
3 changes: 3 additions & 0 deletions satellite/src/device-types/razer-stream-controller-x.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ export class RazerStreamControllerXWrapper extends EventEmitter<WrappedSurfaceEv

async close(): Promise<void> {
this.#queue?.abort()

await this.#deck.blankDevice(true, true).catch(() => null)

await this.#deck.close()
}
async initDevice(client: CompanionClient, status: string): Promise<void> {
Expand Down
3 changes: 3 additions & 0 deletions satellite/src/device-types/streamdeck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ export class StreamDeckWrapper extends EventEmitter<WrappedSurfaceEvents> implem

async close(): Promise<void> {
this.#queue?.abort()

await this.#deck.resetToLogo().catch(() => null)

await this.#deck.close()
}
async initDevice(client: CompanionClient, status: string): Promise<void> {
Expand Down
13 changes: 11 additions & 2 deletions satellite/src/device-types/xencelabs-quick-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ class QuickKeysPluginDetection
extends EventEmitter<SurfacePluginDetectionEvents<XencelabsQuickKeys>>
implements SurfacePluginDetection<XencelabsQuickKeys>
{
initialised = false

async triggerScan(): Promise<void> {
if (!this.initialised) return
// TODO - or should this go the other route and use openDevicesFromArray?
await XencelabsQuickKeysManagerInstance.scanDevices()
}
Expand All @@ -40,15 +43,21 @@ export class QuickKeysPlugin implements SurfacePlugin<XencelabsQuickKeys> {
readonly detection = new QuickKeysPluginDetection()

async init(): Promise<void> {
if (this.detection.initialised) return

this.detection.initialised = true

XencelabsQuickKeysManagerInstance.on('connect', this.#connectListener)
XencelabsQuickKeysManagerInstance.on('disconnect', this.#disconnectListener)
}
async destroy(): Promise<void> {
this.detection.initialised = false

XencelabsQuickKeysManagerInstance.off('connect', this.#connectListener)
XencelabsQuickKeysManagerInstance.off('disconnect', this.#disconnectListener)

// Ensure all devices are closed?
// await XencelabsQuickKeysManagerInstance.closeAll()
// Ensure all devices are closed
await XencelabsQuickKeysManagerInstance.closeAll()
}

#connectListener = (surface: XencelabsQuickKeys) => {
Expand Down
27 changes: 26 additions & 1 deletion satellite/src/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ import {
ApiConfigDataUpdateElectron,
ApiStatusResponse,
ApiSurfaceInfo,
ApiSurfacePluginInfo,
ApiSurfacePluginsEnabled,
compileConfig,
compileStatus,
updateConfig,
updateSurfacePluginsEnabledConfig,
} from './apiTypes.js'
import { fileURLToPath } from 'url'
import { MdnsAnnouncer } from './mdnsAnnouncer.js'
import debounceFn from 'debounce-fn'

const appConfig = new electronStore<SatelliteConfig>({
// schema: satelliteConfigSchema,
Expand All @@ -39,12 +43,20 @@ console.log('Starting')
const webRoot = fileURLToPath(new URL(app.isPackaged ? '../../webui' : '../../webui/dist', import.meta.url))

const client = new CompanionSatelliteClient({ debug: true })
const surfaceManager = await SurfaceManager.create(client)
const surfaceManager = await SurfaceManager.create(client, appConfig.get('surfacePluginsEnabled'))
const server = new RestServer(webRoot, appConfig, client, surfaceManager)
const mdnsAnnouncer = new MdnsAnnouncer(appConfig)

appConfig.onDidChange('remoteIp', () => tryConnect())
appConfig.onDidChange('remotePort', () => tryConnect())
appConfig.onDidChange(
'surfacePluginsEnabled',
debounceFn(() => surfaceManager.updatePluginsEnabled(appConfig.get('surfacePluginsEnabled')), {
wait: 50,
after: true,
before: false,
}),
)

client.on('log', (l) => console.log(l))
client.on('error', (e) => console.error(e))
Expand Down Expand Up @@ -268,6 +280,19 @@ ipcMain.handle('saveConfig', async (_e, newConfig: ApiConfigDataUpdateElectron):
ipcMain.handle('connectedSurfaces', async (): Promise<ApiSurfaceInfo[]> => {
return surfaceManager.getOpenSurfacesInfo()
})
ipcMain.handle('surfacePlugins', async (): Promise<ApiSurfacePluginInfo[]> => {
return surfaceManager.getAvailablePluginsInfo()
})
ipcMain.handle('surfacePluginsEnabled', async (): Promise<ApiSurfacePluginsEnabled> => {
return appConfig.get('surfacePluginsEnabled')
})
ipcMain.handle(
'surfacePluginsEnabledUpdate',
async (_e, newConfig: ApiSurfacePluginsEnabled): Promise<ApiSurfacePluginsEnabled> => {
updateSurfacePluginsEnabledConfig(appConfig, newConfig)
return appConfig.get('surfacePluginsEnabled')
},
)

// about window
ipcMain.handle('openShell', async (_e, url: string): Promise<void> => {
Expand Down
6 changes: 6 additions & 0 deletions satellite/src/electronPreload.cts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type {
ApiConfigDataUpdateElectron,
ApiStatusResponse,
ApiSurfaceInfo,
ApiSurfacePluginInfo,
ApiSurfacePluginsEnabled,
SatelliteUiApi,
// @ts-expect-error weird interop between cjs and mjs
} from './apiTypes.js'
Expand All @@ -17,6 +19,10 @@ const electronApi: SatelliteUiApi = {
saveConfig: async (newConfig: ApiConfigDataUpdateElectron): Promise<ApiConfigData> =>
ipcRenderer.invoke('saveConfig', newConfig),
connectedSurfaces: async (): Promise<ApiSurfaceInfo[]> => ipcRenderer.invoke('connectedSurfaces'),
surfacePlugins: async (): Promise<ApiSurfacePluginInfo[]> => ipcRenderer.invoke('surfacePlugins'),
surfacePluginsEnabled: async (): Promise<ApiSurfacePluginsEnabled> => ipcRenderer.invoke('surfacePluginsEnabled'),
surfacePluginsEnabledUpdate: async (newConfig: ApiSurfacePluginsEnabled): Promise<ApiSurfacePluginsEnabled> =>
ipcRenderer.invoke('surfacePluginsEnabledUpdate', newConfig),
}

contextBridge.exposeInMainWorld('electronApi', electronApi)
Expand Down
Loading

0 comments on commit 2752c4c

Please sign in to comment.