First Start

First Start base 3.0.1
This commit is contained in:
knicolas22 2025-06-19 10:00:16 +02:00
commit f524d63cd9
20 changed files with 9622 additions and 0 deletions

View File

@ -0,0 +1,18 @@
name: Companion Module Checks
on:
push:
jobs:
check:
name: Check module
if: ${{ !contains(github.repository, 'companion-module-template-') }}
permissions:
packages: read
uses: bitfocus/actions/.github/workflows/module-checks.yaml@main
# with:
# upload-artifact: true # uncomment this to upload the built package as an artifact to this workflow that you can download and share with others

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

BIN
.yarn/install-state.gz Normal file

Binary file not shown.

1
.yarnrc.yml Normal file
View File

@ -0,0 +1 @@
nodeLinker: node-modules

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Bitfocus AS - Open Source
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# companion-module-pixap-pixtimerpro
See [HELP.md](./companion/HELP.md) and [LICENSE](./LICENSE)

395
actions.js Normal file
View File

@ -0,0 +1,395 @@
const { Regex } = require('@companion-module/base')
module.exports = {
getActions() {
const actions = {}
let CHOICES_TIMER_PRESETS = []
Object.keys(this.pixtimerdata.presets).forEach((key) => {
// console.log(key, this.pixtimerdata.presets[key])
CHOICES_TIMER_PRESETS.push({
label: this.pixtimerdata.presets[key].name,
id: this.pixtimerdata.presets[key].id,
sort: this.pixtimerdata.presets[key].id,
})
})
let CHOICES_MESSAGE_PRESETS = []
Object.keys(this.pixtimerdata.messages).forEach((key) => {
// console.log(key, this.pixtimerdata.messages[key])
CHOICES_MESSAGE_PRESETS.push({
label: this.pixtimerdata.messages[key].name,
id: this.pixtimerdata.messages[key].id,
sort: this.pixtimerdata.messages[key].id,
})
})
// BLACKOUT
let CHOICES_BLACKOUT = []
Object.keys(this.pixtimerdata.blackoutArray).forEach((key) => {
CHOICES_BLACKOUT.push({
label: this.pixtimerdata.blackoutArray[key].name,
id: this.pixtimerdata.blackoutArray[key].id,
sort: this.pixtimerdata.blackoutArray[key].id,
})
})
// PBP GOTO
let CHOICES_PBP_GOTO = []
Object.keys(this.pixtimerdata.gotoArray).forEach((key) => {
CHOICES_PBP_GOTO.push({
label: this.pixtimerdata.gotoArray[key].name,
id: this.pixtimerdata.gotoArray[key].id,
sort: this.pixtimerdata.gotoArray[key].id,
})
})
// TIMER
actions['recall_timer_preset'] = {
name: 'Recall timer preset',
options: [
{
id: 'recall_timer_preset',
type: 'dropdown',
label: 'Cue',
minChoicesForSearch: 5,
choices: CHOICES_TIMER_PRESETS.sort((a, b) => a.sort - b.sort),
default: '0',
},
],
callback: async (event) => {
this.log('info', 'Recall timer preset >>>', event.options.recall_timer_preset)
await this.sendCommand('PST ' + event.options.recall_timer_preset)
},
}
actions['speaker_timer_play'] = {
name: 'Speaker Timer Play',
options: [],
callback: async (event) => {
this.log('info', 'Timer speaker play !')
await this.sendCommand('PST PLAY')
},
}
actions['speaker_timer_pause'] = {
name: 'Speaker Timer Pause',
options: [],
callback: async (event) => {
this.log('info', 'Timer speaker Pause !')
await this.sendCommand('PST BREAK')
},
}
actions['speaker_timer_stop'] = {
name: 'Speaker Timer Stop',
options: [],
callback: async (event) => {
this.log('info', 'Timer speaker stop !')
await this.sendCommand('PST STOP')
},
}
actions['speaker_timer_flash'] = {
name: 'Speaker Timer Flash',
options: [],
callback: async (event) => {
this.log('info', 'Timer speaker Flash !')
await this.sendCommand('PST FLASH')
},
}
actions['session_timer_play'] = {
name: 'Session Timer Play',
options: [],
callback: async (event) => {
this.log('info', 'Timer session play !')
await this.sendCommand('PSTS PLAY')
},
}
actions['session_timer_pause'] = {
name: 'Session Timer Pause',
options: [],
callback: async (event) => {
this.log('info', 'Timer session Pause !')
await this.sendCommand('PSTS BREAK')
},
}
actions['session_timer_stop'] = {
name: 'Session Timer Stop',
options: [],
callback: async (event) => {
this.log('info', 'Timer session stop !')
await this.sendCommand('PSTS STOP')
},
}
actions['session_timer_flash'] = {
name: 'Session Timer Flash',
options: [],
callback: async (event) => {
this.log('info', 'Timer session flash !')
await this.sendCommand('PSTS FLASH')
},
}
actions['all_timer_play'] = {
name: 'All Timer Play',
options: [],
callback: async (event) => {
this.log('info', 'Timer all play !')
await this.sendCommand('PSTA PLAY')
},
}
actions['all_timer_pause'] = {
name: 'All Timer Pause',
options: [],
callback: async (event) => {
this.log('info', 'Timer all Pause !')
await this.sendCommand('PSTA BREAK')
},
}
actions['all_timer_stop'] = {
name: 'All Timer Stop',
options: [],
callback: async (event) => {
this.log('info', 'Timer all stop !')
await this.sendCommand('PSTA STOP')
},
}
// MESSAGE
actions['recall_message_preset'] = {
name: 'Recall message preset',
options: [
{
id: 'recall_message_preset',
type: 'dropdown',
label: 'Cue',
minChoicesForSearch: 5,
choices: CHOICES_MESSAGE_PRESETS.sort((a, b) => a.sort - b.sort),
default: '0',
},
],
callback: async (event) => {
//this.log('info', 'Recall messsage preset >>>', event.options.recall_message_preset)
await this.sendCommand('PSTM ' + event.options.recall_message_preset)
},
}
actions['message_show'] = {
name: 'Message show',
options: [],
callback: async (event) => {
//this.log('info', `Message show >>> ${this.pixtimerdata.states.messageShow}`)
if (this.pixtimerdata.states.messageShow === 0) {
await this.sendCommand('PSTM SHOW')
} else {
await this.sendCommand('PSTM HIDE')
}
},
}
// BLACKOUT
actions['blackout'] = {
name: 'Blackout',
options: [],
callback: async (event) => {
//this.log('info', `ACTIONS Black show >>> ${this.pixtimerdata.states.blackout}`)
if (this.pixtimerdata.states.blackout === 0) {
await this.sendCommand('BLACK SHOW')
} else {
await this.sendCommand('BLACK HIDE')
}
},
}
// SPEAKER AJUSTE TIME
actions['speaker_adjust_time'] = {
name: 'Adjust Speaker Timer',
options: [
{
type: 'textinput',
label: 'Time in second',
id: 'time',
regex: this.REGEX_SIGNED_NUMBER,
},
],
callback: async (event) => {
//this.log('info', 'Adjust Speaker Timer !')
await this.sendCommand('ADJT ' + event.options.time)
},
};
// TIMER CLOCK
actions['timer_clock'] = {
name: 'Switch Timer clock',
options: [],
callback: async (event) => {
//this.log('info', 'Switch Timer & clock !')
await this.sendCommand('SWTC')
},
}
// EXTERNAL TIMER
actions['external_timer_Play'] = {
name: 'External Timer and Play',
options: [
{
type: 'textinput',
label: 'External Timer and Play',
id: 'time',
default: " 00:00:00",
regex: "/^(0*[0-9]|1[0-9]|2[0-4]):(0*[0-9]|[1-5][0-9]|60):(0*[0-9]|[1-5][0-9]|60)$/",
required: true,
}
],
callback: async (event) => {
//this.log('info', 'External Timer and Play !')
await this.sendCommand('CTD ' + event.options.time + " PLAY")
},
}
actions['external_timer'] = {
name: 'External Timer',
options: [
{
type: 'textinput',
label: 'External Timer',
id: 'time',
default: " 00:00:00",
regex: "/^(0*[0-9]|1[0-9]|2[0-4]):(0*[0-9]|[1-5][0-9]|60):(0*[0-9]|[1-5][0-9]|60)$/",
required: true,
}
],
callback: async (event) => {
//this.log('info', 'External Timer !')
await this.sendCommand('CTD ' + event.options.time)
},
}
actions['external_play'] = {
name: 'External Play',
options: [],
callback: async (event) => {
this.log('info', 'External Play !')
await this.sendCommand('CTD PLAY' )
},
}
actions['external_stop'] = {
name: 'External Stop',
options: [],
callback: async (event) => {
this.log('info', 'External Stop !')
await this.sendCommand('CTD STOP')
},
}
// NDI
actions['ndi_startStop'] = {
name: 'NDI start & stop',
options: [],
callback: async (event) => {
this.log('info', 'NDI start & stop !')
await this.sendCommand('NDI ED')
},
}
// PBP+
actions['pbp_main_enable'] = {
name: 'PlaybackPro main enable',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro main enable !')
await this.sendCommand('PBPM ED')
},
}
actions['pbp_backup_enable'] = {
name: 'PlaybackPro backup enable',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro backup enable !')
await this.sendCommand('PBPB ED')
},
}
actions['pbp_master_take'] = {
name: 'PlaybackPro master take',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master take !')
await this.sendCommand('PBPG TAKE')
},
}
actions['pbp_master_endall'] = {
name: 'PlaybackPro master endall',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master endall !')
await this.sendCommand('PBPG ENDALL')
},
}
actions['pbp_master_previous'] = {
name: 'PlaybackPro master previous',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master previous !')
await this.sendCommand('PBPG PREVC')
},
}
actions['pbp_master_next'] = {
name: 'PlaybackPro master next',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master next !')
await this.sendCommand('PBPG NEXTC')
},
}
actions['pbp_master_play'] = {
name: 'PlaybackPro master play',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master play !')
await this.sendCommand('PBPG PLAY')
},
}
actions['pbp_master_pause'] = {
name: 'PlaybackPro master pause',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master pause !')
await this.sendCommand('PBPG PAUSE')
},
}
actions['pbp_master_goto10'] = {
name: 'PlaybackPro master goto 10',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master goto 10 !')
await this.sendCommand('PBPG GOTO 10')
},
}
actions['pbp_master_goto20'] = {
name: 'PlaybackPro master goto 20',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master goto 20 !')
await this.sendCommand('PBPG GOTO 20')
},
}
actions['pbp_master_goto30'] = {
name: 'PlaybackPro master goto 30',
options: [],
callback: async (event) => {
this.log('info', 'PlaybackPro master goto 30 !')
await this.sendCommand('PBPG GOTO 30')
},
}
actions['pbp_master_goto'] = {
name: 'PlaybackPro master goto',
options: [
{
id: 'pbp_master_goto',
type: 'dropdown',
label: 'Goto',
minChoicesForSearch: 5,
choices: CHOICES_PBP_GOTO.sort((a, b) => a.sort - b.sort),
default: '0',
},
],
callback: async (event) => {
this.log('info', 'PlaybackPro master goto 30 !')
await this.sendCommand('PBPG GOTO ' + event.options.pbp_master_goto)
},
}
return actions
},
}

65
companion/HELP.md Normal file
View File

@ -0,0 +1,65 @@
# piXap piXtimer Pro
Controls piXap piXtimer Pro
Version control 3.0.2
## Configuration
* piXtimer Pro Version 1.9.9 at least
* Type in the IP address of the device.
* The default Port is 9756.
## Actions 1.10
* Speaker Flash
* Session Flash
## Presets 1.10
* Speaker Flash
* Session Flash
## Actions 1.9.9
* Recall timer preset
* Timer speaker play/pause/stop
* Timer session play/pause/stop
* Timer all play/pause/stop
* Recall message preset
* Message show/hide
* Black show/hide
* Set Countdown video time and play
* Set Countdown video time
* Countdown video play/stop
* Adjust speaker timer time
* Switch to Timer & clock
* NDI Enable Disable
* PlaybackPro main enable & disable
* PlaybackPro backup enable & disable
* PlaybackPro general take
* PlaybackPro general end all
* PlaybackPro general previous clip
* PlaybackPro general next clip
* PlaybackPro general play
* PlaybackPro general pause
* PlaybackPro general goto 10 20 30
## Presets 1.9.9
* Recall timer preset
* Timer speaker play/pause/stop
* Timer session play/pause/stop
* Timer all play/pause/stop
* Recall message preset
* Message show/hide
* Black show/hide
* Set Countdown video time and play
* Set Countdown video time
* Countdown video play/stop
* Adjust speaker timer time
* Switch to Timer & clock
* NDI Enable Disable
* PlaybackPro main enable & disable
* PlaybackPro backup enable & disable
* PlaybackPro general take
* PlaybackPro general end all
* PlaybackPro general previous clip
* PlaybackPro general next clip
* PlaybackPro general play
* PlaybackPro general pause
* PlaybackPro general goto 10 20 30

33
companion/manifest.json Normal file
View File

@ -0,0 +1,33 @@
{
"id": "pixap-pixtimerpro",
"name": "pixap-pixtimerpro",
"shortname": "piXtimer Pro",
"description": "piXtimer Pro plugin for Companion",
"version": "3.0.1",
"license": "MIT",
"repository": "git+https://github.com/bitfocus/companion-module-pixap-pixtimerpro.git",
"bugs": "https://github.com/bitfocus/companion-module-pixap-pixtimerpro/issues",
"maintainers": [
{
"name": "Nicolas Keesst",
"email": "nkeesst@pixap.fr"
}
],
"runtime": {
"type": "node22",
"api": "nodejs-ipc",
"apiVersion": "0.0.0",
"entrypoint": "../main.js"
},
"legacyIds": [
"pixtimerpro"
],
"manufacturer": "piXap",
"products": [
"piXtimer Pro"
],
"keywords": [
"Software",
"Timing"
]
}

61
configFields.js Normal file
View File

@ -0,0 +1,61 @@
const { Regex } = require('@companion-module/base')
module.exports = {
getConfigFields() {
return [
{
id: 'info',
type: 'static-text',
width: 12,
label: 'Control Information',
value:
'This module controls an piXtimer Pro. <a href="https://www.pixap.fr/products.php?ref=8&lang=en" target="_new">Product</a>',
},
{
type: 'textinput',
id: 'host',
label: 'Target IP',
width: 8,
regex: Regex.IP,
},
{
type: 'textinput',
id: 'port',
label: 'Control Port',
width: 4,
regex: Regex.PORT,
min: 1,
max: 65535,
default: '9756',
},
{
id: 'timerPortInfo',
type: 'static-text',
width: 11,
label: 'Timer Port Information',
value: 'Before activating this option, check if you have the Dungle connected to piXtimer Pro !',
},
{
type: 'static-text',
id: 'io',
width: 7,
},
{
type: 'checkbox',
id: 'enableTimerPort',
label: 'Enable',
width: 1,
default: false,
},
{
type: 'textinput',
id: 'portTimer',
label: 'Timer Port',
width: 4,
regex: Regex.PORT,
min: 1,
max: 65535,
default: '9758',
},
]
},
}

228
feedbacks.js Normal file
View File

@ -0,0 +1,228 @@
const { combineRgb } = require('@companion-module/base')
module.exports = async function (self) {
self.setFeedbackDefinitions({
speaker_play_status: {
name: 'Speaker change style based on Stop/Play/Pause status',
type: 'boolean',
label: 'Speaker Stop/Play/Pause status',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'speaker_play',
type: 'dropdown',
label: 'Status',
default: 0,
choices: [
{ id: 0, label: 'Stop' },
{ id: 1, label: 'Playing' },
{ id: 2, label: 'Paused' },
],
},
],
callback: (feedback) => {
//console.log('FEEDBACK | SPEAKER PLAY STATUS >>>', feedback.options.speaker_play)
if (self.pixtimerdata.states.speakerPlaying === feedback.options.speaker_play) {
return true
}
},
},
session_play_status: {
name: 'Session change style based on Stop/Play/Pause status',
type: 'boolean',
label: 'Session Stop/Play/Pause status',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'session_play',
type: 'dropdown',
label: 'Status',
default: 0,
choices: [
{ id: 0, label: 'Stop' },
{ id: 1, label: 'Playing' },
{ id: 2, label: 'Paused' },
],
},
],
callback: (feedback) => {
//console.log('FEEDBACK | SESSION PLAY STATUS >>>', feedback.options.session_play)
if (self.pixtimerdata.states.sessionPlaying === feedback.options.session_play) {
return true
}
},
},
all_play_status: {
name: 'All change style based on Stop/Play/Pause status',
type: 'boolean',
label: 'All Stop/Play/Pause status',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'all_play',
type: 'dropdown',
label: 'Status',
default: 0,
choices: [
{ id: 0, label: 'Stop' },
{ id: 1, label: 'Playing' },
{ id: 2, label: 'Paused' },
],
},
],
callback: (feedback) => {
//console.log('FEEDBACK | ALL PLAY STATUS >>>', feedback.options.all_play)
if (self.pixtimerdata.states.allPlaying === feedback.options.all_play) {
return true
}
},
},
current_speaker: {
name: 'Current Speaker ID Change',
type: 'boolean',
label: 'Current Speaker ID Change',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'current_speaker',
type: 'number',
label: 'ID',
default: 0,
},
],
callback: (feedback) => {
//console.log('FEEDBACK | Current Speaker ID Change >>>', feedback.options.current_speaker)
if (self.pixtimerdata.states.currentSpeaker === feedback.options.current_speaker) {
return true
}
},
},
current_session: {
name: 'Current Session ID Change',
type: 'boolean',
label: 'Current Session ID Change',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'current_session',
type: 'number',
label: 'ID',
default: 0,
},
],
callback: (feedback) => {
//console.log('FEEDBACK | Current Session ID Change >>>', feedback.options.current_session)
if (self.pixtimerdata.states.currentSession === feedback.options.current_session) {
return true
}
},
},
blackout: {
name: 'Blackout Show/Hide',
type: 'boolean',
label: 'Blackout Show/Hide',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'blackout',
type: 'number',
label: 'Status',
default: 0,
},
],
callback: (feedback) => {
//console.log('FEEDBACK | Blackout Change >>>', feedback.options.blackout)
if (self.pixtimerdata.states.blackout === feedback.options.blackout) {
return true
}
},
},
message_show: {
name: 'Message Show/Hide',
type: 'boolean',
label: 'Message Show/Hide',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'message_show',
type: 'number',
label: 'Status',
default: 0,
},
],
callback: (feedback) => {
//console.log('FEEDBACK | MESSAGE SHOW Change >>>', feedback.options.message_show)
if (self.pixtimerdata.states.messageShow === feedback.options.message_show) {
return true
}
},
},
switch_timer_clock: {
name: 'Switch Timer Clock',
type: 'boolean',
label: 'Switch Timer Clock',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'switch_timer_clock',
type: 'number',
label: 'Status',
default: 0,
},
],
callback: (feedback) => {
//console.log('FEEDBACK | MESSAGE SHOW Change >>>', feedback.options.message_show)
if (self.pixtimerdata.states.switchTimerClock === feedback.options.switch_timer_clock) {
return true
}
},
},
ndi_start: {
name: 'NDI Start',
type: 'boolean',
label: 'NDI Start',
defaultStyle: {
bgcolor: combineRgb(255, 0, 0),
color: combineRgb(255, 255, 255),
},
options: [
{
id: 'ndi_start',
type: 'number',
label: 'Status',
default: 0,
},
],
callback: (feedback) => {
//console.log('FEEDBACK | NDI START >>>', feedback.options.ndi_start)
if (self.pixtimerdata.states.ndiStart === feedback.options.ndi_start) {
return true
}
},
},
})
}

390
main.js Normal file
View File

@ -0,0 +1,390 @@
const { InstanceBase, Regex, runEntrypoint, InstanceStatus, TCPHelper, combineRgb } = require('@companion-module/base')
const configFields = require('./configFields')
const presets = require('./presets')
const actions = require('./actions')
const pixtimerpro = require('./pixap-pixtimerpro')
const UpgradeScripts = require('./upgrades')
const UpdateFeedbacks = require('./feedbacks')
const UpdateVariableDefinitions = require('./variables')
class PixtimerProInstance extends InstanceBase {
constructor(internal) {
super(internal)
Object.assign(this, {
...configFields,
...presets,
...actions,
...pixtimerpro,
})
this.adjustArray = [1, 5, 10, 20, 30]
}
async init(config) {
this.config = config
this.pixtimerdata = {
presets: [],
messages: [],
gotoArray: [
{ id: 10, name: 'Goto 10' },
{ id: 20, name: 'Goto 20' },
{ id: 30, name: 'Goto 30' },
],
blackoutArray: [
{ id: 'toogle', name: 'Toogle' },
{ id: 'show', name: 'Show' },
{ id: 'hide', name: 'Hide' },
],
states: {},
}
this.updateStatus(InstanceStatus.Ok)
this.updateFeedbacks() // export feedbacks
this.updateVariableDefinitions() // export variable definitions
this.init_tcp()
this.refreshTcpTimer()
this.setVariableValues({ speaker_timer: '00:00:00' })
this.setVariableValues({ speaker_timer_ms: '00:00' })
this.setVariableValues({ speaker_timer_h: '00' })
this.setVariableValues({ speaker_timer_m: '00' })
this.setVariableValues({ speaker_timer_s: '00' })
this.setVariableValues({ session_timer: '00:00:00' })
this.setVariableValues({ session_timer_ms: '00:00' })
this.setVariableValues({ session_timer_h: '00' })
this.setVariableValues({ session_timer_m: '00' })
this.setVariableValues({ session_timer_s: '00' })
this.setVariableValues({ external_timer: '00:00:00' })
this.setVariableValues({ external_timer_ms: '00:00' })
this.setVariableValues({ external_timer_h: '00' })
this.setVariableValues({ external_timer_m: '00' })
this.setVariableValues({ external_timer_s: '00' })
}
refreshTcpTimer() {
if (!this.config.enableTimerPort) {
if (this.socketTimer !== undefined) {
this.socketTimer.destroy()
delete this.socketTimer
}
} else this.init_tcp_timer()
}
// When module gets deleted
async destroy() {
this.socket.destroy()
this.socketTimer.destroy()
this.log('info', 'destroy', this.id)
}
async configUpdated(config) {
let resetConnection = false
let resetConnectionTimer = false
if (this.config.host != config.host) {
resetConnection = true
}
if (this.config.enableTimerPort) {
resetConnectionTimer = true
} else {
resetConnectionTimer = true
}
this.config = config
if (resetConnection || !this.socket) {
this.init_tcp()
}
if (resetConnectionTimer || !this.socketTimer) {
this.refreshTcpTimer()
}
this.setActionDefinitions(this.getActions())
this.setPresetDefinitions(this.getPresets())
}
async sendCommand(cmd) {
if (cmd) {
this.log('debug', `sending ${cmd} to ${this.config.host}`)
await this.socket.send(cmd)
}
}
updateFeedbacks() {
UpdateFeedbacks(this)
}
updateVariableDefinitions() {
UpdateVariableDefinitions(this)
}
init_tcp() {
this.log('info', `PIXTIMER PRO | INIT TCP CONTROL >>> ${this.config.host} ${this.config.port}`)
if (this.socket !== undefined) {
this.socket.destroy()
delete this.socket
}
if (this.config.port === undefined) {
this.config.port = 9756
}
if (this.config.host) {
//!= undefined
this.log('info', `TCP CONTROL | Opening connection to ${this.config.host}:${this.config.port}`)
this.socket = new TCPHelper(this.config.host, this.config.port, { reconnect: true })
this.socket.on('status_change', (status, message) => {
this.updateStatus(status, message)
//this.log('info', `TCP CONTROL | Status update >>> ${status} | ${message}`)
})
this.socket.on('error', (err) => {
this.updateStatus(InstanceStatus.UnknownError, err.message)
this.log('error', 'TCP CONTROL | Network error: ' + err.message)
})
let receivebuffer = ''
this.socket.on('connect', () => {
this.updateStatus(InstanceStatus.Ok)
//this.log('info', 'Connected')
receivebuffer = ''
this.socket.send('NC\n').catch((e) => {
this.log('error', `TCP CONTROL | Socket error: ${e}`)
})
})
this.socket.on('data', (chunk) => {
let i = 0
let line = ''
let offset = 0
receivebuffer += chunk.toString()
while ((i = receivebuffer.indexOf('\n', offset)) !== -1) {
line = receivebuffer.slice(offset, i)
//this.log('info', 'RECEIVE DATA LINE >>> ' + line.toString())
offset = i + 1
this.socket.emit('receiveline', line)
}
receivebuffer = receivebuffer.slice(offset)
//this.log('info', 'RECEIVE DATA >>> ' + receivebuffer.toString())
})
this.socket.on('receiveline', (data) => {
//const info = data.toString().split("/")
this.log('warn', `TCP CONTROL | RECEIVE LINE >>> ${data.toString()}`)
// TIMER LIST
if (data.toString().slice(0, 5) === 'timer') {
this.pixtimerdata.presets = this.getTimerPresetList(data)
this.setActionDefinitions(this.getActions())
this.setPresetDefinitions(this.getPresets())
}
// MESSAGE LIST
if (data.toString().slice(0, 7) === 'message') {
this.pixtimerdata.messages = this.getMessagePresetList(data)
this.setActionDefinitions(this.getActions())
this.setPresetDefinitions(this.getPresets())
}
if (data.toString().slice(0, 3) === 'pts') {
// CURRENT PRESET SESSION
//this.log('info', `TCP CONTROL | RECEIVE LINE | CURRENT SESSION PRESET >>> ${data.toString().slice(4)}`)
this.pixtimerdata.states.currentSession = parseInt(data.toString().slice(4))
this.setVariableValues({ session_current: this.pixtimerdata.states.currentSession })
this.checkFeedbacks('current_session')
} else if (data.toString().slice(0, 2) === 'pt') {
// CURRENT PRESET SPEAKER
//this.log('info', `TCP CONTROL | RECEIVE LINE | CURRENT SPEAKER PRESET >>> ${data.toString().slice(3)}`)
this.pixtimerdata.states.currentSpeaker = parseInt(data.toString().slice(3))
this.setVariableValues({ speaker_current: this.pixtimerdata.states.currentSpeaker })
this.checkFeedbacks('current_speaker')
}
// MESSAGE SHOW
if (data.toString().slice(0, 3) === 'cmv') {
//this.log('info', `TCP CONTROL | RECEIVE LINE | MESSAGE SHOW >>> ${data.toString().slice(4)}`)
this.pixtimerdata.states.messageShow = parseInt(data.toString().slice(4))
this.setVariableValues({ message_show: this.pixtimerdata.states.messageShow })
this.checkFeedbacks('message_show')
}
// SWITCH TIMER CLOCK
if (data.toString().slice(0, 4) === 'SWTC') {
//this.log('error', `TCP CONTROL | RECEIVE LINE | SWITCH TIMER CLOCK >>> ${data.toString().slice(5)}`)
this.pixtimerdata.states.switchTimerClock = parseInt(data.toString().slice(5))
this.setVariableValues({ switch_timer_clock: this.pixtimerdata.states.switchTimerClock })
this.checkFeedbacks('switch_timer_clock')
}
// SPEAKER PLAY
if (data.toString().slice(0, 4) === 'play') {
//this.log('error', `TCP CONTROL | RECEIVE LINE | SPEAKER PLAY STATUS >>> ${data.toString().slice(5)}`)
this.pixtimerdata.states.speakerPlaying = parseInt(data.toString().slice(5))
this.setVariableValues({ speaker_play_status: this.pixtimerdata.states.speakerPlaying })
this.checkFeedbacks('speaker_play_status')
}
// SESSION PLAY
if (data.toString().slice(0, 5) === 'splay') {
//this.log('error', `TCP CONTROL | RECEIVE LINE | SPEAKER PLAY STATUS >>> ${data.toString().slice(6)}`)
this.pixtimerdata.states.sessionPlaying = parseInt(data.toString().slice(6))
this.setVariableValues({ session_play_status: this.pixtimerdata.states.sessionPlaying })
this.checkFeedbacks('session_play_status')
} else if (data.toString().slice(0, 5) === 'black') {
this.log('error', `TCP CONTROL | RECEIVE LINE | BLACKOUT STATUS >>> ${data.toString().slice(6)}`)
if (data.toString().slice(6) === "false") {
this.pixtimerdata.states.blackout = 0
} else if (data.toString().slice(6) === "true") {
this.pixtimerdata.states.blackout = 1
} else this.pixtimerdata.states.blackout = parseInt(data.toString().slice(6))
this.setVariableValues({ blackout: this.pixtimerdata.states.blackout })
this.checkFeedbacks('blackout')
}
// NDI
if (data.toString().slice(0, 5) === 'NDIST') {
//this.log('error', `TCP CONTROL | RECEIVE LINE | NDI STATUS >>> ${data.toString().slice(6)}`)
this.pixtimerdata.states.ndiStart = parseInt(data.toString().slice(6))
this.setVariableValues({ ndi_start: this.pixtimerdata.states.ndiStart })
this.checkFeedbacks('ndi_start')
}
// ALL PLAYING
if (this.pixtimerdata.states.speakerPlaying === 1 && this.pixtimerdata.states.sessionPlaying === 1) {
this.pixtimerdata.states.allPlaying = 1
} else if (this.pixtimerdata.states.speakerPlaying === 2 && this.pixtimerdata.states.sessionPlaying === 2) {
this.pixtimerdata.states.allPlaying = 2
} else {
this.pixtimerdata.states.allPlaying = 0
}
this.setVariableValues({ all_play_status: this.pixtimerdata.states.allPlaying })
this.checkFeedbacks('all_play_status')
if (data.toString().slice(0, 2) === 'pn') {
// PROJECT NAME
//this.log('info', `TCP CONTROL | RECEIVE LINE | PROJECT NAME >>> ${data.toString().slice(3)}`)
this.pixtimerdata.states.projectName = data.toString().slice(3)
this.setVariableValues({ project_name: this.pixtimerdata.states.projectName })
//this.checkFeedbacks('current_speaker')
}
})
}
}
// TCP TIMER
init_tcp_timer() {
this.log('info', `PIXTIMER PRO | INIT TCP TIMER >>> ${this.config.host} ${this.config.portTimer}`)
if (this.socketTimer !== undefined) {
this.socketTimer.destroy()
delete this.socketTimer
}
if (this.config.portTimer === undefined) {
this.config.portTimer = 9758
}
if (this.config.host) {
//!= undefined
this.log('info', `TCP TIMER | Opening timer connection to ${this.config.host}:${this.config.portTimer}`)
this.socketTimer = new TCPHelper(this.config.host, this.config.portTimer, { reconnect: true })
this.socketTimer.on('status_change', (status, message) => {
this.updateStatus(status, message)
//this.log('info', `TCP CONTROL | Status update >>> ${status} | ${message}`)
})
this.socketTimer.on('error', (err) => {
this.updateStatus(InstanceStatus.UnknownError, err.message)
this.log('error', `TCP TIMER | Network error >>> ${err.message} | Please Check Dungle`)
})
let receivebuffer = ''
this.socketTimer.on('connect', () => {
this.updateStatus(InstanceStatus.Ok)
//this.log('info', 'TCP TIMER | TIMER Connected')
receivebuffer = ''
this.socketTimer.send('NC\n').catch((e) => {
this.log('error', `TCP TIMER | Socket error: ${e}`)
})
this.setVariableValues({ external_timer: 'STOP' })
this.setVariableValues({ external_timer_ms: 'STOP' })
this.setVariableValues({ external_timer_h: 'STOP' })
this.setVariableValues({ external_timer_m: 'STOP' })
this.setVariableValues({ external_timer_s: 'STOP' })
})
this.socketTimer.on('data', (chunk) => {
let i = 0
let line = ''
let offset = 0
receivebuffer += chunk.toString()
while ((i = receivebuffer.indexOf('\n', offset)) !== -1) {
line = receivebuffer.slice(offset, i)
//this.log('info', 'TCP TIMER | RECEIVE TIMER DATA LINE >>> ' + line.toString())
offset = i + 1
this.socketTimer.emit('receiveTimerline', line)
}
receivebuffer = receivebuffer.slice(offset)
//this.log('info', 'TCP TIMER | RECEIVE TIMER DATA >>> ' + receivebuffer.toString())
})
this.socketTimer.on('receiveTimerline', (data) => {
//const info = data.toString().split("/")
//this.log('warn', `TCP TIMER | RECEIVE TIMER LINE >>> ${data.toString()}`)
if (data.toString().slice(0, 1) === 'H') {
this.log('warn', `RECEIVE TIMER LINE >>> ${data.toString().slice(2)}`)
let speakerTimerArray = data.toString().slice(2).split(':')
this.setVariableValues({ speaker_timer: data.toString().slice(2) })
this.setVariableValues({ speaker_timer_ms: `${speakerTimerArray[1]}:${speakerTimerArray[2]}` })
this.setVariableValues({ speaker_timer_h: speakerTimerArray[0] })
this.setVariableValues({ speaker_timer_m: speakerTimerArray[1] })
this.setVariableValues({ speaker_timer_s: speakerTimerArray[2] })
}
if (data.toString().slice(0, 1) === 'S') {
//this.log('warn', `TCP TIMER | RECEIVE TIMER LINE >>> ${data.toString().slice(2)}`)
let sessionTimerArray = data.toString().slice(2).split(':')
this.setVariableValues({ session_timer: data.toString().slice(2) })
this.setVariableValues({ session_timer_ms: `${sessionTimerArray[1]}:${sessionTimerArray[2]}` })
this.setVariableValues({ session_timer_h: sessionTimerArray[0] })
this.setVariableValues({ session_timer_m: sessionTimerArray[1] })
this.setVariableValues({ session_timer_s: sessionTimerArray[2] })
}
if (data.toString().slice(0, 1) === 'V') {
//this.log('warn', `TCP TIMER | RECEIVE EXTERNAL TIMER LINE >>> ${data.toString()}`) // || data.toString().slice(2) === "STOP"
let externalTimerArray = data.toString().slice(2).split(':')
if (data.toString().slice(2) === 'stop') {
this.setVariableValues({ external_timer: 'STOP' })
this.setVariableValues({ external_timer_ms: 'STOP' })
this.setVariableValues({ external_timer_h: 'STOP' })
this.setVariableValues({ external_timer_m: 'STOP' })
this.setVariableValues({ external_timer_s: 'STOP' })
} else {
this.setVariableValues({
external_timer: `${externalTimerArray[0]}:${externalTimerArray[1]}:${externalTimerArray[2]}`,
})
this.setVariableValues({ external_timer_ms: `${externalTimerArray[1]}:${externalTimerArray[2]}` })
this.setVariableValues({ external_timer_h: externalTimerArray[0] })
this.setVariableValues({ external_timer_m: externalTimerArray[1] })
this.setVariableValues({ external_timer_s: externalTimerArray[2] })
}
}
})
}
}
}
runEntrypoint(PixtimerProInstance, UpgradeScripts)

3819
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "pixtimerPro",
"version": "3.0.2",
"main": "main.js",
"scripts": {
"format": "prettier -w .",
"package": "companion-module-build"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/bitfocus/companion-module-pixap-pixtimerpro.git"
},
"dependencies": {
"@companion-module/base": "~1.11.3"
},
"devDependencies": {
"@companion-module/tools": "^2.3.0",
"prettier": "^3.5.3"
},
"prettier": "@companion-module/tools/.prettierrc.json",
"packageManager": "yarn@4.9.1"
}

58
pixap-pixtimerpro.js Normal file
View File

@ -0,0 +1,58 @@
module.exports = {
getTimerPresetList(data){
let timers = data.toString().slice(6).split('/')
let lp = []
//this.log('info', 'RECEIVE TIMERS >>> ' + timers)
for (let i = 0; i < timers.length; i++) {
let p = timers[i].split(':')
//this.log('info', `Timer >>> ${p[0].toString()} ${p[1].toString()} ${p[2].toString()} ${p[3].toString()}`)
let ps = {}
ps.id = parseInt(p[0])
ps.color = this.hexToRgb(p[1])
ps.name = p[2]
ps.speaker = parseInt(p[3])
//this.log('info', `GET TIMER PRESET LIST >>> ${JSON. stringify(ps)}`)
lp.push(ps)
}
//this.log('info', `GET TIMER PRESET LIST >>> ${JSON. stringify(lp)}`)
return lp
},
getMessagePresetList(data){
let messages = data.toString().slice(8).split('/')
let lm = []
//this.log('info', 'RECEIVE TIMERS >>> ' + timers)
for (let i = 0; i < messages.length; i++) {
let p = messages[i].split(':')
//this.log('info', `Timer >>> ${p[0].toString()} ${p[1].toString()} ${p[2].toString()} ${p[3].toString()}`)
let pm = {}
pm.id = parseInt(p[0])
pm.color = this.hexToRgb(p[1])
pm.name = p[2]
//this.log('info', `GET MESSAGE PRESET LIST >>> ${JSON. stringify(pm)}`)
lm.push(pm)
}
//this.log('info', `GET TIMER PRESET LIST >>> ${JSON. stringify(lm)}`)
return lm
},
hexToRgb(hex) {
// Vérifier si l'entrée est une chaîne de caractères valide
if (typeof hex !== 'string' || !/^#?[0-9A-Fa-f]{6}$/.test(hex)) {
return null;
}
// Retirer le préfixe "#" si présent
if (hex.startsWith('#')) {
hex = hex.slice(1);
}
// Convertir la chaîne hexadécimale en nombres entiers RGB
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
// Retourner la valeur RGB sous forme d'objet
return [ r, g, b ];
}
}

1272
presets.js Normal file

File diff suppressed because it is too large Load Diff

1226
test.js Normal file

File diff suppressed because it is too large Load Diff

13
upgrades.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = [
/*
* Place your upgrade scripts here
* Remember that once it has been added it cannot be removed!
*/
function (context, props) {
return {
updatedConfig: null,
updatedActions: [],
updatedFeedbacks: [],
}
},
]

37
variables.js Normal file
View File

@ -0,0 +1,37 @@
module.exports = async function (self) {
self.setVariableDefinitions([
{ variableId: 'project_name', name: "Project Name"},
{ variableId: 'speaker_play_status', name: "Speaker Play Status"},
{ variableId: 'speaker_current', name: "Speaker Current Preset"},
{ variableId: 'speaker_timer', name: "Speaker Timer H:M:S"},
{ variableId: 'speaker_timer_ms', name: "Speaker Timer M:S"},
{ variableId: 'speaker_timer_h', name: "Speaker Timer hour"},
{ variableId: 'speaker_timer_m', name: "Speaker Timer minute"},
{ variableId: 'speaker_timer_s', name: "Speaker Timer second"},
{ variableId: 'session_play_status', name: "Session Play Status"},
{ variableId: 'session_current', name: "Session Current Preset"},
{ variableId: 'session_timer', name: 'Session Timer H:M:S' },
{ variableId: 'session_timer_ms', name: "Session Timer M:S"},
{ variableId: 'session_timer_h', name: "Session Timer hour"},
{ variableId: 'session_timer_m', name: "Session Timer minute"},
{ variableId: 'session_timer_s', name: "Session Timer second"},
{ variableId: 'all_play_status', name: "All Play Status"},
{ variableId: 'external_timer', name: "External Timer H:M:S"},
{ variableId: 'external_timer_ms', name: "External Timer M:S"},
{ variableId: 'external_timer_h', name: "External Timer hour"},
{ variableId: 'external_timer_m', name: "External Timer minute"},
{ variableId: 'external_timer_s', name: "External Timer second"},
{ variableId: 'blackout', name: "Blackout"},
{ variableId: 'message_show', name: "Message Show/Hide"},
{ variableId: 'switch_timer_clock', name: "Switch Timer Clock"},
{ variableId: 'ndi_start', name: "NDI Start/Stop"},
])
}

1958
yarn.lock Normal file

File diff suppressed because it is too large Load Diff