Skip to content

Commit

Permalink
feat: expand rest api to serve minimal interface (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
Julusian authored Feb 6, 2024
1 parent ed43061 commit 2074eac
Show file tree
Hide file tree
Showing 41 changed files with 4,728 additions and 277 deletions.
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"license": "MIT",
"private": true,
"scripts": {
"prepare": "husky install",
"dev": "ts-node src/main.ts",
"install": "./node_modules/.bin/husky install",
"dev": "ts-node src/main.ts config.json",
"dev-electron": "run build:main && electron dist/electron.js",
"build": "rimraf dist && run build:main",
"build:main": "tsc -p tsconfig.build.json",
Expand All @@ -35,9 +35,9 @@
"devDependencies": {
"@electron/notarize": "^2.2.1",
"@sofie-automation/code-standard-preset": "~2.5.2",
"@types/electron-prompt": "^1.6.5",
"@types/koa": "^2.14.0",
"@types/koa-router": "^7.4.8",
"@types/koa-static": "^4.0.4",
"@types/node": "^18.19.14",
"@types/node-hid": "^1.3.4",
"@types/semver": "^7.5.6",
Expand All @@ -59,18 +59,19 @@
"@julusian/skia-canvas": "^1.0.5",
"@loupedeck/node": "^1.0.0",
"@xencelabs-quick-keys/node": "^1.0.0",
"conf": "^10.2.0",
"electron-about-window": "^1.15.2",
"electron-prompt": "^1.7.0",
"electron-store": "^8.1.0",
"eventemitter3": "^4.0.7",
"exit-hook": "^2.2.1",
"infinitton-idisplay": "^1.1.2",
"koa": "^2.15.0",
"koa-body": "^6.0.1",
"koa-router": "^12.0.1",
"koa-static": "^5.0.0",
"meow": "^9.0.0",
"node-hid": "^3.0.0",
"semver": "^7.5.4",
"semver": "^7.6.0",
"tslib": "^2.6.2",
"usb": "^2.11.0"
},
Expand Down
14 changes: 11 additions & 3 deletions pi-image/satellite-config
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
#
# Since v1.7.0 of satellite this is no longer the main config file.
# It is read at startup to import configuration to the main config file.
# After it has been read, it gets reset back to defaults.
#
# More options are available in the web interface.
#

# Set this to the ip address or hostname of your companion installation
# examples:
# - COMPANION_IP=192.168.100.1
# - COMPANION_IP=companion.example.org
COMPANION_IP=127.0.0.1
# COMPANION_IP=127.0.0.1

# If you are connecting through a router or firewall which has remapped the port, you will need to change that here to match
COMPANION_PORT=16622
# COMPANION_PORT=16622

# Port for the REST server (0 to disable)
REST_PORT=9999
# REST_PORT=9999
8 changes: 3 additions & 5 deletions pi-image/satellite.service
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ Wants=network-online.target
Type=simple
User=satellite
WorkingDirectory=/usr/local/src/companion-satellite
EnvironmentFile=-/boot/satellite-config
Environment=SATELLITE_CONFIG_PATH=/boot/satellite-config
ExecStartPre=+/usr/local/src/companion-satellite/pi-image/fixup-config.sh
ExecStart=/opt/fnm/aliases/default/bin/node /usr/local/src/companion-satellite/dist/main.js $COMPANION_IP $COMPANION_PORT $REST_PORT
Restart=always
ExecStartPre=+/opt/fnm/aliases/default/bin/node /usr/local/src/companion-satellite/dist/fixup-pi-config.js /home/satellite/satellite-config.json
ExecStart=/opt/fnm/aliases/default/bin/node /usr/local/src/companion-satellite/dist/main.js /home/satellite/satellite-config.json
Restart=on-failure
KillSignal=SIGINT
TimeoutStopSec=60

Expand Down
43 changes: 43 additions & 0 deletions src/apiTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Conf from 'conf'
import { CompanionSatelliteClient } from './client'
import { SatelliteConfig } from './config'

export interface ApiStatusResponse {
connected: boolean
companionVersion: string | null
companionApiVersion: string | null
}

export interface ApiConfigData {
host: string
port: number

httpEnabled: boolean
httpPort: number
}

export function compileStatus(client: CompanionSatelliteClient): ApiStatusResponse {
return {
connected: client.connected,
companionVersion: client.companionVersion,
companionApiVersion: client.companionApiVersion,
}
}

export function compileConfig(appConfig: Conf<SatelliteConfig>): ApiConfigData {
return {
host: appConfig.get('remoteIp'),
port: appConfig.get('remotePort'),

httpEnabled: appConfig.get('restEnabled'),
httpPort: appConfig.get('restPort'),
}
}

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

if (newConfig.httpEnabled !== undefined) appConfig.set('restEnabled', newConfig.httpEnabled)
if (newConfig.httpPort !== undefined) appConfig.set('restPort', newConfig.httpPort)
}
16 changes: 14 additions & 2 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ export type CompanionSatelliteClientEvents = {
error: [Error]
log: [string]
connected: []
connecting: []
disconnected: []
ipChange: [host: string, port: number]

draw: [DeviceDrawProps]
brightness: [{ deviceId: string; percent: number }]
Expand Down Expand Up @@ -107,6 +107,9 @@ export class CompanionSatelliteClient extends EventEmitter<CompanionSatelliteCli
private _registeredDevices = new Set<string>()
private _pendingDevices = new Map<string, number>() // Time submitted

private _companionVersion: string | null = null
private _companionApiVersion: string | null = null

public forceSplitEncoders = false

public get host(): string {
Expand All @@ -118,6 +121,12 @@ export class CompanionSatelliteClient extends EventEmitter<CompanionSatelliteCli
public get connected(): boolean {
return this._connected
}
public get companionVersion(): string | null {
return this._companionVersion
}
public get companionApiVersion(): string | null {
return this._companionApiVersion
}

public get capabilities(): ClientCapabilities {
return {
Expand Down Expand Up @@ -225,7 +234,7 @@ export class CompanionSatelliteClient extends EventEmitter<CompanionSatelliteCli
this._connectionActive = true

setImmediate(() => {
this.emit('ipChange', host, port)
this.emit('connecting')
})

this._host = host
Expand Down Expand Up @@ -319,6 +328,9 @@ export class CompanionSatelliteClient extends EventEmitter<CompanionSatelliteCli
}

private handleBegin(params: Record<string, string | boolean>): void {
this._companionVersion = typeof params.CompanionVersion === 'string' ? params.CompanionVersion : null
this._companionApiVersion = typeof params.ApiVersion === 'string' ? params.ApiVersion : null

const protocolVersion = params.ApiVersion
if (typeof protocolVersion === 'string') {
if (semver.lte('1.3.0', protocolVersion)) {
Expand Down
60 changes: 60 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Conf, { Schema } from 'conf'
import path from 'path'

export interface SatelliteConfig {
remoteIp: string
remotePort: number

restEnabled: boolean
restPort: number
}

export const satelliteConfigSchema: Schema<SatelliteConfig> = {
remoteIp: {
type: 'string',
description: 'Address of Companion installation',
default: '127.0.0.1',
},
remotePort: {
type: 'integer',
description: 'Port number of Companion installation',
minimum: 1,
maximum: 65535,
default: 16622,
},

restEnabled: {
type: 'boolean',
description: 'Enable HTTP api',
default: true,
},
restPort: {
type: 'integer',
description: 'Port number of run HTTP server on',
minimum: 1,
maximum: 65535,
default: 9999,
},
}

export function ensureFieldsPopulated(store: Conf<SatelliteConfig>): void {
for (const [key, schema] of Object.entries<any>(satelliteConfigSchema)) {
if (store.get(key) === undefined && schema.default !== undefined) {
// Ensure values are written to disk
store.set(key, schema.default)
}
}
}

export function openHeadlessConfig(rawConfigPath: string): Conf<SatelliteConfig> {
const absoluteConfigPath = path.isAbsolute(rawConfigPath) ? rawConfigPath : path.join(process.cwd(), rawConfigPath)

const appConfig = new Conf<SatelliteConfig>({
schema: satelliteConfigSchema,
configName: path.parse(absoluteConfigPath).name,
projectName: 'companion-satellite',
cwd: path.dirname(absoluteConfigPath),
})
ensureFieldsPopulated(appConfig)
return appConfig
}
2 changes: 1 addition & 1 deletion src/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export class DeviceManager {

this.showStatusCard('Connecting', true)
})
client.on('ipChange', () => {
client.on('connecting', () => {
this.showStatusCard('Connecting', true)
})

Expand Down
Loading

0 comments on commit 2074eac

Please sign in to comment.