Skip to content

Commit

Permalink
V1.1.1 (#24)
Browse files Browse the repository at this point in the history
* Issue #13, moved all actions into actions subfolder, added actions/upstreamKeyActions
git commit -am Issue

* formatting of eslint.config.mjs

* fixed build error

* #15 fixed lint errors

* formatting

* store major update work

* added jest config

* missed directoryu

* added audioMixer dunction

* missed files

* added downstreamKeyer function

* moved more actions into functions , fixed missing presets and feedbacks. Linted all.

* moved last functions to Functions folder,moved data handling to actions.ts instead of state.ts

* added variables files and system

* fix: moved devDependencies to Dev Deps and removed version from Manifest

* finished refactoring of the connection parts of code

* fixes some missed issues

* Fixed some issues found when reviewing 1.1.0. Also removed some config fields that were unnessesary and added bonjour discovery

* removed lock file

* removed som unused files and regenerated yarn.lock

---------

Co-authored-by: Justin James <digitaldrummerj@gmail.com>
  • Loading branch information
fa1k3n and digitaldrummerj authored Jan 9, 2025
1 parent 871f881 commit 9f2ae97
Show file tree
Hide file tree
Showing 20 changed files with 745 additions and 1,185 deletions.
Binary file not shown.
10 changes: 8 additions & 2 deletions companion/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "osee-gostream",
"shortname": "gostream",
"description": "Companion module for GoStream products",
"version": "1.0.0",
"version": "0.0.0",
"license": "MIT",
"repository": "git+https://github.com/bitfocus/companion-module-osee-gostream.git",
"bugs": "https://github.com/bitfocus/companion-module-osee-gostream/issues",
Expand All @@ -22,5 +22,11 @@
},
"manufacturer": "OSEE",
"products": ["GoStream"],
"keywords": ["GoStream", "companion"]
"keywords": ["GoStream", "companion"],
"bonjourQueries": {
"bonjourDevices": {
"type": "oseectrl",
"protocol": "tcp"
}
}
}
7 changes: 1 addition & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "companion-module-osee-gostreamdeck",
"version": "1.1.0",
"version": "1.1.1",
"main": "./dist/index.js",
"license": "MIT",
"prettier": "@companion-module/tools/.prettierrc.json",
Expand All @@ -11,7 +11,6 @@
"build": "rimraf dist && yarn build::main",
"build::main": "tsc -p tsconfig.build.json",
"build::dev": "tsc -p tsconfig.build.json --watch",
"test": "jest",
"prepare": "husky",
"doc": "npx typedoc --out docs src"
},
Expand All @@ -28,13 +27,9 @@
},
"devDependencies": {
"@companion-module/tools": "^2.1.0",
"@types/jest": "^29.5.13",
"eslint": "9.14.0",
"husky": "^9.1.6",
"jest": "^29.7.0",
"jest-extended": "^4.0.2",
"prettier": "^3.3.3",
"ts-jest": "^29.2.5",
"typedoc": "^0.26.8",
"typescript": "~5.5.4",
"typescript-eslint": "^8.8.1"
Expand Down
53 changes: 14 additions & 39 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { Regex, SomeCompanionConfigField } from '@companion-module/base'
import { ALL_MODEL_CHOICES } from './models/index'
export const portDefault = 19010

export interface Config {
host: string
port: number
reconnect: boolean
reconnectInterval: number
}

export function GetConfigFields(): SomeCompanionConfigField[] {
Expand All @@ -15,29 +12,14 @@ export function GetConfigFields(): SomeCompanionConfigField[] {
label: 'Information',
id: 'info',
type: 'static-text',
value: 'This module controls GoSteamDeck over IP protocol',
value: 'This module controls a GoStream device over IP protocol',
width: 12,
},
{
label: 'GoSteamDeck target IP',
id: 'host',
type: 'textinput',
default: '192.168.1.80',
width: 6,
required: true,
regex: Regex.IP,
tooltip: 'GoSteamDeck address',
},
{
label: 'GoSteamDeck target port ',
id: 'port',
type: 'number',
default: exports.portDefault,
id: 'bonjourDevices',
type: 'bonjour-device',
label: 'Device',
width: 6,
min: 1,
max: 0xffff,
step: 1,
tooltip: 'Usually 19010 by default',
},
{
type: 'dropdown',
Expand All @@ -46,25 +28,18 @@ export function GetConfigFields(): SomeCompanionConfigField[] {
width: 6,
choices: ALL_MODEL_CHOICES,
default: 0,
isVisible: (options) => !options['bonjourDevices'],
},
{
label: 'Reconnect',
id: 'reconnect',
type: 'checkbox',
default: true,
width: 4,
tooltip: 'Chose if you want Companion to try to reconnect to GoSteamDeck when the connection is lost.',
},
{
label: 'Reconnect interval (seconds)',
id: 'reconnectInterval',
type: 'number',
min: 1,
max: 60,
default: 5,
width: 4,
isVisible: (config) => config.reconnect === true,
tooltip: 'The interval in seconds between each reconnect attempt.',
label: 'Target IP',
id: 'host',
type: 'textinput',
default: '192.168.1.80',
width: 6,
required: true,
regex: Regex.IP,
tooltip: 'GoSteamDeck address',
isVisible: (options) => !options['bonjourDevices'],
},
]
}
66 changes: 36 additions & 30 deletions src/connection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { TCPHelper, InstanceStatus } from '@companion-module/base'
import { portDefault } from './config'
import { ReqType } from './enums'
import crc16modbus from 'crc/crc16modbus'

Expand Down Expand Up @@ -27,11 +26,12 @@ let partialPacketBuffer: Buffer | null = null

const PACKET_HEADER_SIZE = 5
const PACKET_HEAD = new Uint8Array([HEAD1, HEAD2])
const PORT_NUMBER = 19010

export type GoStreamData = {
id: string
type: string
value: number[]
value?: string | number | any[]
}

/*
Expand All @@ -40,7 +40,7 @@ export type GoStreamData = {
* U16 HEADER = 0xA6 0xEB
* U8 protoid
* U16 length
* U8[] data
* GoStreamData[] data
* U16 crc
*
* A TCP datagram might contain many data packets
Expand All @@ -59,13 +59,8 @@ export function connect(instance: GoStreamInstance): void {
tcp.destroy()
}
instance.updateStatus(InstanceStatus.Connecting)
const host = instance.config.host
const port = instance.config.port
const option = {
reconnect_interval: instance.config.reconnectInterval,
reconnect: instance.config.reconnect,
}
tcp = new TCPHelper(host, port || portDefault, option)
const host = instance.config.bonjourDevices ? instance.config.bonjourDevices.split(':')[0] : instance.config.host
tcp = new TCPHelper(host, PORT_NUMBER)
tcp.on('status_change', (state, message) => {
instance.updateStatus(state, message)
instance.log('debug', 'Socket reconnected')
Expand All @@ -85,29 +80,35 @@ export function connect(instance: GoStreamInstance): void {
})
tcp.on('data', (msg_data) => {
let index = msg_data.indexOf(PACKET_HEAD)

// Take care of data before start of packet, i.e. if index > 0
// needs to be merged with hopefully saved data
if (index > 0) {
// OR packets that dont have a head i.e. index < 0
// The data needs to be merged with hopefully saved data
if (index !== 0) {
if (partialPacketBuffer != null) {
const packet_data = Buffer.alloc(partialPacketBuffer.length + index, partialPacketBuffer)
const remaining_data = msg_data.subarray(0, index)
remaining_data.copy(packet_data, partialPacketBuffer.length)
ParaData(instance, packet_data)
partialPacketBuffer = null
if (index > 0) {
// Process packet_data and continue
ParaData(instance, packet_data)
partialPacketBuffer = null
} else if (index < 0) {
// Save packet_data as it is not complete yet
partialPacketBuffer = Buffer.from(packet_data)
return // No more data
}
} else {
console.log('ERROR, packet out of order')
console.error('packet data out of order, dropping packet!')
return
}
}

// Consume all packet data
while (index >= 0) {
while (index === 0) {
const packet_size = msg_data.readUInt16LE(index + 3)
const packet_data = msg_data.subarray(index, index + PACKET_HEADER_SIZE + packet_size)
ParaData(instance, packet_data)
index = msg_data.indexOf(PACKET_HEAD, index + PACKET_HEADER_SIZE + packet_size)
if (index + PACKET_HEADER_SIZE + packet_size > msg_data.length) {
// Partial data found, save in buffer for next datagram
partialPacketBuffer = Buffer.alloc(msg_data.length - index, msg_data.subarray(index))
break
}
Expand Down Expand Up @@ -157,6 +158,20 @@ export function disconnectSocket(): void {
}
}

export async function sendCommands(commands: GoStreamData[]): Promise<boolean> {
if (tcp !== null) {
const cmdStrings: string[] = []
commands.forEach((cmd) => {
const json = JSON.stringify(cmd)
const bufs = Buffer.from(json, 'utf-8')
cmdStrings.push(PackData(bufs).toString())
})
const sign = await tcp.send(cmdStrings.join(''))
return sign
}
return false
}

export async function sendCommand(id: string, type: ReqType, value?: string | number | any[]): Promise<boolean> {
if (tcp !== null) {
const obj = { id: id, type: type, value: value }
Expand All @@ -169,26 +184,17 @@ export async function sendCommand(id: string, type: ReqType, value?: string | nu
return false
}

// Convert number to UInt16 LE
function num2UInt16LE(num: number): number[] {
const numStr = num.toString(16).padStart(4, '0')
return [parseInt(numStr.substring(2, 4), 16), parseInt(numStr.substring(0, 2), 16)]
}
function PackData(data: Buffer): Buffer {
const packetLen = data.length + 7
const packet = Buffer.alloc(packetLen)

packet[0] = HEAD1
packet[1] = HEAD2
packet[2] = ProType
const packetSize = num2UInt16LE(packetLen - 5)
packet[3] = packetSize[0]
packet[4] = packetSize[1]
packet.writeUInt16LE(packetLen - 5, 3)

if (data != undefined) data.copy(packet, 5, 0, data.length)
const packetCrc = num2UInt16LE(crc16modbus(packet.subarray(0, packetLen - 2)))
packet[packet.length - 2] = packetCrc[0]
packet[packet.length - 1] = packetCrc[1]
packet.writeUInt16LE(crc16modbus(packet.subarray(0, packetLen - 2)), packet.length - 2)
return packet
}
function UpackDatas(resp: Buffer): Buffer {
Expand Down
1 change: 1 addition & 0 deletions src/functions/audioMixer/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export function create(instance: GoStreamInstance): CompanionActionDefinitions {
}

export function handleData(instance: GoStreamInstance, data: GoStreamData): boolean {
if (!data.value) return false
switch (data.id as ActionId) {
case ActionId.AudioTransition:
instance.states.AudioMixer.transitionEnabled = data.value[0] === 1 ? true : false
Expand Down
5 changes: 3 additions & 2 deletions src/functions/downstreamKeyer/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,14 +293,15 @@ export function create(instance: GoStreamInstance): CompanionActionDefinitions {
}

export function handleData(instance: GoStreamInstance, data: GoStreamData): boolean {
if (!data.value) return false
switch (data.id as ActionId) {
case ActionId.DskSourceFill: {
const select = getChoices(ActionType.DskSourceFill).find((s) => s.id === data.value[0])
const select = getChoices(ActionType.DskSourceFill).find((s) => data.value && s.id === data.value[0])
if (select !== undefined) instance.states.DownstreamKeyer.fill = select
return true
}
case ActionId.DskSourceKey: {
const select = getChoices(ActionType.DskSourceFill).find((s) => s.id === data.value[0])
const select = getChoices(ActionType.DskSourceFill).find((s) => s.id === data.value && data.value[0])
if (select !== undefined) instance.states.DownstreamKeyer.key = select
return true
}
Expand Down
1 change: 1 addition & 0 deletions src/functions/live/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function create(instance: GoStreamInstance): CompanionActionDefinitions {
}

export function handleData(instance: GoStreamInstance, data: GoStreamData): boolean {
if (!data.value) return false
switch (data.id as ActionId) {
case ActionId.Live: {
instance.states.Live.State = data.value[0]
Expand Down
1 change: 1 addition & 0 deletions src/functions/macro/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export function create(_instance: GoStreamInstance): CompanionActionDefinitions
}
}
export function handleData(instance: GoStreamInstance, data: GoStreamData): boolean {
if (!data.value) return false
switch (data.id as ActionId) {
case ActionId.MacroInfo: {
const obj = {
Expand Down
1 change: 1 addition & 0 deletions src/functions/mixEffect/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ export function create(instance: GoStreamInstance): CompanionActionDefinitions {
}

export function handleData(instance: GoStreamInstance, data: GoStreamData): boolean {
if (!data.value) return false
switch (data.id as ActionId) {
case ActionId.PvwIndex: {
if (data.value[0] !== undefined) {
Expand Down
29 changes: 16 additions & 13 deletions src/functions/mixEffect/state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ActionId } from './actionId'
import { sendCommand } from '../../connection'
import { sendCommands } from '../../connection'
import { ReqType } from '../../enums'
import { Choice } from '../../choices'

Expand Down Expand Up @@ -56,16 +56,19 @@ export function create(): MixEffectState {
}

export async function sync(): Promise<void> {
await sendCommand(ActionId.PgmIndex, ReqType.Get)
await sendCommand(ActionId.PvwIndex, ReqType.Get)
await sendCommand(ActionId.AutoTransition, ReqType.Get)
await sendCommand(ActionId.Prev, ReqType.Get)
await sendCommand(ActionId.FTB, ReqType.Get)
await sendCommand(ActionId.FtbRate, ReqType.Get)
await sendCommand(ActionId.FtbAudioAFV, ReqType.Get)
await sendCommand(ActionId.TransitionIndex, ReqType.Get)
await sendCommand(ActionId.TransitionRate, ReqType.Get, [0])
await sendCommand(ActionId.TransitionRate, ReqType.Get, [1])
await sendCommand(ActionId.TransitionRate, ReqType.Get, [2])
await sendCommand(ActionId.TransitionSource, ReqType.Get)
const cmds = [
{ id: ActionId.PgmIndex, type: ReqType.Get },
{ id: ActionId.PvwIndex, type: ReqType.Get },
{ id: ActionId.AutoTransition, type: ReqType.Get },
{ id: ActionId.Prev, type: ReqType.Get },
{ id: ActionId.FTB, type: ReqType.Get },
{ id: ActionId.FtbRate, type: ReqType.Get },
{ id: ActionId.FtbAudioAFV, type: ReqType.Get },
{ id: ActionId.TransitionIndex, type: ReqType.Get },
{ id: ActionId.TransitionRate, type: ReqType.Get, value: [0] },
{ id: ActionId.TransitionRate, type: ReqType.Get, value: [1] },
{ id: ActionId.TransitionRate, type: ReqType.Get, value: [2] },
{ id: ActionId.TransitionSource, type: ReqType.Get },
]
await sendCommands(cmds)
}
12 changes: 6 additions & 6 deletions src/functions/playback/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,24 +190,24 @@ export function create(instance: GoStreamInstance): CompanionActionDefinitions {
export function handleData(instance: GoStreamInstance, data: GoStreamData): boolean {
switch (data.id as ActionId) {
case ActionId.PlaybackMode:
instance.states.Playback.Mode = data.value[0]
instance.states.Playback.Mode = data.value![0]
break
case ActionId.PlaybackRepeat:
instance.states.Playback.Repeat = data.value[0] === 1 ? true : false
instance.states.Playback.Repeat = data.value![0] === 1 ? true : false
break
case ActionId.PlaybackPause:
instance.states.Playback.Pause = data.value[0] === 1 ? true : false
instance.states.Playback.Pause = data.value![0] === 1 ? true : false
//updatePlayStatedVariables(instance, instance.states.PlayBackState.PlaybackPause)
break
case ActionId.PlaybackBar:
instance.states.Playback.Bar = data.value[0] === 1 ? true : false
instance.states.Playback.Bar = data.value![0] === 1 ? true : false
break
case ActionId.PlayFile:
instance.states.Playback.File = instance.states.Playback.FileList.indexOf(data.value[0])
instance.states.Playback.File = instance.states.Playback.FileList.indexOf(data.value![0])
//updatePlayFileVariables(instance, data.value[0])
break
case ActionId.PlaybackList:
instance.states.Playback.FileList = instance.states.Playback.FileList.concat(data.value)
instance.states.Playback.FileList = instance.states.Playback.FileList.concat(data.value!)
// Re-initialize actions and feedbackls so that dropdown are updated
instance.init_actions()
instance.init_feedbacks()
Expand Down
Loading

0 comments on commit 9f2ae97

Please sign in to comment.