From 59bb508fde5b669524baee4b313aeda8382fd32d Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Fri, 5 Jul 2024 17:53:09 +1000 Subject: [PATCH 01/25] v3 upgrade WIP --- .gitignore | 8 +- README.md | 2 +- HELP.md => companion/HELP.md | 7 +- companion/manifest.json | 37 + package.json | 45 +- src/actions.js | 305 +++++ src/config.js | 118 ++ src/consts.js | 17 + src/crosspoints.js | 184 +++ src/feedbacks.js | 179 +++ index.js => src/index.js | 150 ++- src/keepalive.js | 1 + src/labels.js | 77 ++ src/levels.js | 12 + src/names.js | 24 + src/presets.js | 110 ++ src/tcp.js | 220 ++++ src/upgrades.js | 0 src/util.js | 66 + src/variables.js | 99 ++ yarn.lock | 2219 ++++++++++++++++++++++++++++++++++ 21 files changed, 3805 insertions(+), 75 deletions(-) rename HELP.md => companion/HELP.md (96%) create mode 100644 companion/manifest.json create mode 100644 src/actions.js create mode 100644 src/config.js create mode 100644 src/consts.js create mode 100644 src/crosspoints.js create mode 100644 src/feedbacks.js rename index.js => src/index.js (92%) create mode 100644 src/keepalive.js create mode 100644 src/labels.js create mode 100644 src/levels.js create mode 100644 src/names.js create mode 100644 src/presets.js create mode 100644 src/tcp.js create mode 100644 src/upgrades.js create mode 100644 src/util.js create mode 100644 src/variables.js create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore index c27d074..31702cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,10 @@ +.vscode/ node_modules/ package-lock.json +/pkg +/pkg.tgz +*.zip +*.log .nova/ -temp.js \ No newline at end of file +temp.js +DEBUG-* diff --git a/README.md b/README.md index 6ac100b..29fa440 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # companion-module-generic-swp08 -See [HELP.md](./HELP.md) and [LICENSE](./LICENSE) \ No newline at end of file +See [HELP.md](./companion/HELP.md) and [LICENSE](./LICENSE) \ No newline at end of file diff --git a/HELP.md b/companion/HELP.md similarity index 96% rename from HELP.md rename to companion/HELP.md index 0d27685..9857830 100644 --- a/HELP.md +++ b/companion/HELP.md @@ -80,4 +80,9 @@ Some dynamic information is stored in variables which you can access through the ## Version 1.0.5 - Added presets for some actions -- Added feedback Source routed to selected Destination \ No newline at end of file +- Added feedback Source routed to selected Destination + +## Version 2.0.0 +- Update for Companion 3 +- Add Connection Keep Alive +- Add Tx Message Buffer \ No newline at end of file diff --git a/companion/manifest.json b/companion/manifest.json new file mode 100644 index 0000000..54cb96a --- /dev/null +++ b/companion/manifest.json @@ -0,0 +1,37 @@ +{ + "id": "generic-swp08", + "name": "generic-swp08", + "shortname": "SW-P-08", + "description": "General Remote Control Protocol (SW-P-08)", + "version": "0.0.0", + "license": "MIT", + "repository": "git+https://github.com/bitfocus/companion-module-generic-swp08.git", + "bugs": "https://github.com/bitfocus/companion-module-generic-swp08/issues", + "maintainers": [ + { + "name": "Peter Daniel" + } + ], + "runtime": { + "type": "node18", + "api": "nodejs-ipc", + "apiVersion": "0.0.0", + "entrypoint": "../src/main.js" + }, + "legacyIds": [], + "manufacturer": "Generic", + "products": [ + "SW-P-08", + "SW-P-08 Ross Router", + "SW-P-08 Snell ProBel Router", + "SW-P-08 Grass Valley Router", + "SW-P-08 Lawo VSM", + "SW-P-08 Calrec Router", + "SW-P-08 NVision Router", + "SW-P-08 Sony Broadcast Router", + "SW-P-08 BNCS", + "SW-P-08 DirectOut M.1k2" + ], + "keywords": ["Router", "Matrix", "Broadcast", "Production"] + +} diff --git a/package.json b/package.json index 01d88ce..45c63d0 100644 --- a/package.json +++ b/package.json @@ -1,40 +1,23 @@ { "name": "generic-swp08", - "version": "1.0.5", - "api_version": "1.0.0", - "keywords": [ - "Router", - "Matrix", - "Broadcast", - "Production" - ], - "manufacturer": "Generic", - "product": [ - "SW-P-08", - "SW-P-08 Ross Router", - "SW-P-08 Snell ProBel Router", - "SW-P-08 Grass Valley Router", - "SW-P-08 Lawo VSM", - "SW-P-08 Calrec Router", - "SW-P-08 NVision Router", - "SW-P-08 Sony Broadcast Router", - "SW-P-08 BNCS", - "SW-P-08 DirectOut M.1k2" - ], - "shortname": "SW-P-08", - "description": "General Remote Control Protocol (SW-P-08)", - "main": "index.js", + "version": "2.0.0", + "main": "src/index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "format": "prettier --write", + "lint:raw": "eslint --ext .ts --ext .js --ignore-pattern dist --ignore-pattern pkg", + "lint": "yarn lint:raw ." }, - "author": "Peter Daniel", "license": "MIT", - "bugs": { - "url": "https://github.com/bitfocus/companion-module-generic-swp08/issues" - }, - "homepage": "https://github.com/bitfocus/companion-module-generic-swp08#readme", "repository": { "type": "git", "url": "git+https://github.com/bitfocus/companion-module-generic-swp08.git" - } + }, + "dependencies": { + "@companion-module/base": "~1.8.0" + }, + "devDependencies": { + "@companion-module/tools": "^1.5.0" + }, + "prettier": "@companion-module/tools/.prettierrc.json" } diff --git a/src/actions.js b/src/actions.js new file mode 100644 index 0000000..0b9bf59 --- /dev/null +++ b/src/actions.js @@ -0,0 +1,305 @@ +export async function UpdateActions(self) { + let actionDefinitions = [] + + actionDefinitions['select_level'] = { + name: 'Select Levels', + options: [ + { + type: 'multidropdown', + label: 'Levels', + id: 'level', + default: [1], + choices: this.levels, + minSelection: 1, + }, + ], + callback: async ({ options }) => { + self.processLevelsSelection(options.level, true) + }, + } + actionDefinitions['deselect_level'] = { + name: 'De-Select Levels', + options: [ + { + type: 'multidropdown', + label: 'Levels', + id: 'level', + default: [1], + choices: this.levels, + minSelection: 1, + }, + ], + callback: async ({ options }) => { + self.processLevelsSelection(options.level, false) + }, + } + actionDefinitions['toggle_level'] = { + name: 'Toggle Levels', + options: [ + { + type: 'multidropdown', + label: 'Levels', + id: 'level', + default: [1], + choices: this.levels, + minSelection: 1, + }, + ], + callback: async ({ options }) => { + self.processLevelsSelection(options.level, 'toggle') + }, + } + actionDefinitions['select_dest'] = { + name: 'Select Destination', + options: [ + { + type: 'number', + label: 'Destination', + id: 'dest', + default: 1, + min: 1, + max: 65536, + }, + ], + callback: async ({ options }) => { + self.selected_dest = parseInt(options.dest) + self.getCrosspoints(options.dest) + console.log('set destination ' + self.selected_dest) + self.setVariableValues({ Destination : self.selected_dest}) + self.checkFeedbacks('selected_dest', 'selected_level_dest') + }, + } + actionDefinitions['select_dest_name'] = { + name: 'Select Destination name', + options: [ + { + type: 'dropdown', + label: 'Destination', + id: 'dest', + default: 1, + choices: this.dest_names, + }, + ], + callback: async ({ options }) => { + self.selected_dest = parseInt(options.dest) + self.getCrosspoints(options.dest) + console.log('set destination ' + self.selected_dest) + self.setVariableValues({ Destination: self.selected_dest }) + self.checkFeedbacks('selected_dest', 'selected_level_dest') + }, + } + actionDefinitions['select_source'] = { + name: 'Select Source', + options: [ + { + type: 'number', + label: 'Source', + id: 'source', + default: 1, + min: 1, + max: 65536, + }, + ], + callback: async ({ options }) => { + self.selected_source = parseInt(options.source) + console.log('set source ' + self.selected_source) + self.setVariableValues({ Source : self.selected_source }) + self.checkFeedbacks('selected_source') + }, + } + actionDefinitions['select_source_name'] = { + name: 'Select Source name', + options: [ + { + type: 'dropdown', + label: 'Source', + id: 'source', + default: 1, + choices: this.source_names, + }, + ], + callback: async ({ options }) => { + self.selected_source = parseInt(options.source) + console.log('set source ' + self.selected_source) + self.setVariableValues({ Source: self.selected_source }) + self.checkFeedbacks('selected_source') + }, + } + actionDefinitions['route_source'] = { + name: 'Route Source to selected Levels and Destination', + options: [ + { + type: 'number', + label: 'Source', + id: 'source', + default: 1, + min: 1, + max: 65536, + }, + ], + callback: async ({ options }) => { + console.log(self.selected_level) + const l = self.selected_level.length + for (let i = 0; i < l; i++) { + if (self.selected_level[i].enabled === true) { + self.SetCrosspoint(options.source, self.selected_dest, self.selected_level[i].id) + } + } + }, + } + actionDefinitions['route_source_name'] = { + name: 'Route Source name to selected Levels and Destination', + options: [ + { + type: 'dropdown', + label: 'Source', + id: 'source', + default: 1, + choices: this.source_names, + }, + ], + callback: async ({ options }) => { + console.log(self.selected_level) + const l = self.selected_level.length + for (let i = 0; i < l; i++) { + if (self.selected_level[i].enabled === true) { + self.SetCrosspoint(options.source, self.selected_dest, self.selected_level[i].id) + } + } + }, + } + actionDefinitions['take'] = { + name: 'Take', + options: [], + callback: async () => { + console.log(self.selected_level) + const l = self.selected_level.length + for (let i = 0; i < l; i++) { + if (self.selected_level[i].enabled === true) { + self.SetCrosspoint(self.selected_source, self.selected_dest, self.selected_level[i].id) + } + } + }, + } + actionDefinitions['clear'] = { + name: 'Clear', + options: [ + { + type: 'dropdown', + label: 'Clear', + id: 'clear', + default: 'all', + choices: [ + { id: 'all', label: 'All' }, + { id: 'level', label: 'Levels' }, + { id: 'dest', label: 'Destination' }, + { id: 'source', label: 'Source' }, + ], + }, + { + type: 'checkbox', + label: "Enable all levels on 'Clear All' or 'Clear Levels'", + id: 'clear_enable_levels', + default: true, + }, + ], + callback: async ({ options }) => { + if (options.clear === 'all' || options.clear === 'level') { + self.selected_level = [] + for (let i = 1; i <= self.config.max_levels; i++) { + self.selected_level.push({ id: i, enabled: options.clear_enable_levels }) + } + self.checkFeedbacks('selected_level', 'selected_level_dest') + console.log('clear levels') + console.log(self.selected_level) + } + + if (options.clear === 'all' || options.clear === 'dest') { + self.selected_dest = 0 + self.setVariableValues({ Destination : self.selected_dest }) + self.checkFeedbacks('selected_dest', 'selected_level_dest') + console.log('clear dest') + } + + if (options.clear === 'all' || options.clear === 'source') { + self.selected_source = 0 + self.setVariableValues({ Source : self.selected_source }) + self.checkFeedbacks('selected_source', 'clear source') + } + }, + } + actionDefinitions['set_crosspoint'] = { + name: 'Set crosspoint', + options: [ + { + type: 'multidropdown', + label: 'Levels', + id: 'level', + default: [1], + choices: this.levels, + minSelection: 1, + }, + { + type: 'number', + label: 'Source', + id: 'source', + default: 1, + min: 1, + max: 65536, + }, + { + type: 'number', + label: 'Destination', + id: 'dest', + default: 1, + min: 1, + max: 65536, + }, + ], + callback: async ({ options }) => { + for (let level_val of options.level) { + self.SetCrosspoint(options.source, options.dest, level_val) + } + }, + } + actionDefinitions['set_crosspoint_name'] = { + name: 'Set crosspoint by name', + options: [ + { + type: 'multidropdown', + label: 'Levels', + id: 'level', + default: [1], + choices: this.levels, + minSelection: 1, + }, + { + type: 'dropdown', + label: 'Source', + id: 'source', + default: 1, + choices: this.source_names, + }, + { + type: 'dropdown', + label: 'Destination', + id: 'dest', + default: 1, + choices: this.dest_names, + }, + ], + callback: async ({ options }) => { + for (let level_val of options.level) { + self.SetCrosspoint(options.source, options.dest, level_val) + } + }, + } + actionDefinitions['get_names'] = { + name: 'Refresh Source and Destination names', + options: [], + callback: async () => { + self.readNames() + }, + } + self.setActionDefinitions(actionDefinitions) +} diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..ad9be30 --- /dev/null +++ b/src/config.js @@ -0,0 +1,118 @@ +import { Regex } from '@companion-module/base' + +export async function configUpdated(config) { + + this.log('debug','update config') + + this.config = config + + this.updateVariableDefinitions() + this.updateFeedbacks() + this.updateActions() + + this.init_tcp() + this.checkFeedbacks('selected_level', 'selected_level_dest','selected_dest','selected_source') +} + +export function getConfigFields() { + return [ + { + type: 'static-text', + id: 'info', + width: 12, + label: 'Information', + value: 'This module will allow you to control broadcast routers which implement the SW-P-08 standard protocol.', + }, + { + type: 'textinput', + id: 'host', + label: 'Device IP', + width: 6, + regex: Regex.REGEX_IP, + }, + { + type: 'textinput', + id: 'port', + label: 'Device Port', + width: 6, + default: '8910', + regex: Regex.REGEX_PORT, + }, + { + type: 'number', + label: 'Matrix Number (Default 1)', + id: 'matrix', + width: 6, + default: 1, + min: 1, + max: 16, + range: true, + step: 1, + }, + { + type: 'number', + label: 'Number of levels defined in router', + id: 'max_levels', + width: 6, + default: 3, + min: 1, + max: 256, + range: true, + step: 1, + }, + { + type: 'checkbox', + label: 'Enable', + id: 'supported_commands_on_connect', + width: 1, + default: true, + }, + { + type: 'static-text', + label: 'Request supported commands on connection', + id: 'supported_commands_on_connect_txt', + value: 'Not supported by all router controllers. Try disabling this feature if you encounter problems', + width: 11, + }, + { + type: 'checkbox', + label: 'Enable', + id: 'read_names_on_connect', + width: 1, + default: false, + }, + { + type: 'static-text', + label: 'Request names on connection', + id: 'read_names_on_connect_txt', + value: 'Not supported by all router controllers', + width: 11, + }, + { + type: 'checkbox', + label: 'Enable', + id: 'extended_support', + width: 1, + default: false, + }, + { + type: 'static-text', + label: 'Router has more than 1024 source or destination names', + id: 'extended_support_txt', + value: 'Use extended command set for name requests. Not supported by all router controllers', + width: 11, + }, + { + type: 'dropdown', + label: 'Request name length (ignored by some routers)', + id: 'name_chars', + width: 6, + default: '01', + choices: [ + { id: '00', label: '4 characters' }, + { id: '01', label: '8 characters' }, + { id: '02', label: '12 characters' }, + ], + }, + ] +} \ No newline at end of file diff --git a/src/consts.js b/src/consts.js new file mode 100644 index 0000000..d24be32 --- /dev/null +++ b/src/consts.js @@ -0,0 +1,17 @@ +import { combineRgb } from '@companion-module/base' + +export const msgDelay = 5 + +export const DLE = '10' +export const STX = '02' +export const ETX = '03' + +export const colours = { + white: combineRgb(255, 255, 255), + black: combineRgb(0, 0, 0), + red: combineRgb(240, 0, 0), + green: combineRgb(102, 255, 102), + purple: combineRgb(255, 102, 255), + cyan: combineRgb(102, 255, 255), + orange: combineRgb(255, 191, 128) +} diff --git a/src/crosspoints.js b/src/crosspoints.js new file mode 100644 index 0000000..4a3228c --- /dev/null +++ b/src/crosspoints.js @@ -0,0 +1,184 @@ + +export function crosspointConnected (data) { + //let matrix = ((data[1] & 0xf0) >> 4) + 1 unused + let level = (data[1] & 0x0f) + 1 + let destDiv = (data[2] & 0x70) >> 4 + let sourceDiv = data[2] & 0x7 + let dest = 128 * destDiv + data[3] + 1 + let source = 128 * sourceDiv + data[4] + 1 + + console.log('Source ' + source + ' routed to ' + dest + ' on level ' + level) + this.log('debug', 'Source ' + source + ' routed to destination ' + dest + ' on level ' + level) + + this.update_crosspoints(source, dest, level) +} + +export function ext_crosspointConnected (data) { + //let matrix = data[1] + 1 + let level = data[2] + 1 + let destDiv = data[3] * 256 + let destMod = data[4] + let sourceDiv = data[5] * 256 + let sourceMod = data[6] + let dest = destDiv + destMod + 1 + let source = sourceDiv + sourceMod + 1 + + console.log('Source ' + source + ' routed to ' + dest + ' on level ' + level) + this.log('debug', 'Source ' + source + ' routed to destination ' + dest + ' on level ' + level) + + this.update_crosspoints(source, dest, level) +} + +export function update_crosspoints (source, dest, level) { + if (dest == this.selected_dest) { + // update variables for selected dest source + this.setVariableValues({[`Sel_Dest_Source_Level_${level.toString()}`]: source}) + if (this.source_names.length > 0) { + // only if names have been retrieved + try { + this.setVariableValues({ + [`Sel_Dest_Source_Name_Level_${level.toString()}`]: this.stripNumber(this.source_names[source - 1].label), + }) + } catch (e) { + this.log('debug', 'Unable to set Sel_Dest_Source_Name_Level') + } + } + } + + // store route data + for (let i = 0; i < this.routeTable.length; i++) { + if (this.routeTable[i].level === level && this.routeTable[i].dest === dest) { + // update existing + this.routeTable[i].source = source + console.log(this.routeTable) + this.checkFeedbacks('source_dest_route') + return + } + } + + // add new + const new_route = { level: level, dest: dest, source: source } + this.routeTable.push(new_route) + console.log(this.routeTable) + this.checkFeedbacks('source_dest_route') +} + +export function SetCrosspoint (sourceN, destN, levelN) { + let action + this.log('debug', 'Crosspoint ' + sourceN + '>' + destN + ' level ' + levelN) + console.log('SetCrosspoint ' + sourceN + '>' + destN + ' level ' + levelN) + + if (sourceN <= 0 || sourceN > 65536) { + this.log('warn', 'Unable to route source ' + sourceN) + return + } + + if (destN <= 0 || destN > 65536) { + this.log('warn', 'Unable to route destination ' + destN) + return + } + + if (levelN <= 0 || levelN > 256) { + this.log('warn', 'Unable to route level ' + levelN) + return + } + if (sourceN > 1024 || destN > 1024 || levelN > 16) { + // Extended command required + const COM = '82' + // Matrix + const matrix = this.padLeft((this.config.matrix - 1).toString(16), 2) + // Level + const level = this.padLeft((levelN - 1).toString(16), 2) + // Dest DIV 256 + const destDIV = this.padLeft(Math.floor((destN - 1) / 256).toString(16), 2) + // Destination MOD 256 + const destMOD = this.padLeft(((destN - 1) % 256).toString(16), 2) + // Source DIV 256 + const sourceDIV = this.padLeft(Math.floor((sourceN - 1) / 256).toString(16), 2) + // Source MOD 128 + const sourceMOD = this.padLeft(((sourceN - 1) % 256).toString(16), 2) + // Byte count + const count = '07' + // checksum + const checksum = this.checksum8(COM + matrix + level + destDIV + destMOD + sourceDIV + sourceMOD + count) + // message + action = COM + matrix + level + destDIV + destMOD + sourceDIV + sourceMOD + count + checksum + } else { + // Standard Command + const COM = '02' + // Matrix and Level + const matrix = (this.config.matrix - 1) << 4 + const level = levelN - 1 + const matrix_level = this.padLeft((matrix | level).toString(16), 2) + // Multiplier if source or dest > 128 + const destDIV = Math.floor((destN - 1) / 128) + const sourceDIV = Math.floor((sourceN - 1) / 128) + const multiplier = this.padLeft(((destDIV << 4) | sourceDIV).toString(16), 2) + // Destination MOD 128 + const dest = this.padLeft(((destN - 1) % 128).toString(16), 2) + // Source MOD 128 + const source = this.padLeft(((sourceN - 1) % 128).toString(16), 2) + // Byte count + const count = '05' + // checksum + const checksum = this.checksum8(COM + matrix_level + multiplier + dest + source + count) + // message + action = COM + matrix_level + multiplier + dest + source + count + checksum + } + + this.sendMessage(action) +} + +export function getCrosspoints (destN) { + console.log('GetCrosspoint ' + destN) + + if (destN <= 0 || destN > 65536) { + this.log('warn', 'Unable to get crosspoint destination ' + destN) + return + } + + if (this.config.max_levels > 16 || destN > 1024) { + // Extended commands + const COM = '81' + // Byte count + const count = '05' + // Matrix + const matrix = this.padLeft((this.config.matrix - 1).toString(16), 2) + // Dest DIV 256 + const destDIV = this.padLeft(Math.floor((destN - 1) / 256).toString(16), 2) + // Dest Mod 256 + const destMOD = this.padLeft(((destN - 1) % 256).toString(16), 2) + + // check all levels + for (let i = 0; i <= this.config.max_levels - 1; i++) { + const level = this.padLeft(i.toString(16), 2) + // checksum + const checksum = this.checksum8(COM + matrix + level + destDIV + destMOD + count) + // message + const action = COM + matrix + level + destDIV + destMOD + count + checksum + this.sendMessage(action) + } + } else { + // Standard commands + const COM = '01' + // Byte count + const count = '04' + // Matrix and Level + const matrix = (this.config.matrix - 1) << 4 + // Multiplier if dest > 128 + const destDIV = Math.floor((destN - 1) / 128) + const multiplier = this.padLeft((destDIV << 4).toString(16), 2) + // Destination MOD 128 + const dest = this.padLeft(((destN - 1) % 128).toString(16), 2) + + // check all levels + for (let i = 0; i <= this.config.max_levels - 1; i++) { + const matrix_level = this.padLeft((matrix | i).toString(16), 2) + // checksum + const checksum = this.checksum8(COM + matrix_level + multiplier + dest + count) + // message + const action = COM + matrix_level + multiplier + dest + count + checksum + this.sendMessage(action) + } + } +} \ No newline at end of file diff --git a/src/feedbacks.js b/src/feedbacks.js new file mode 100644 index 0000000..669500c --- /dev/null +++ b/src/feedbacks.js @@ -0,0 +1,179 @@ +import { colours } from './consts.js' + + +export async function UpdateFeedbacks(self) { + // feedback + let feedbacks = {} + + feedbacks['selected_level'] = { + type: 'boolean', + label: 'Selected Levels', + description: 'Change colour of button on selected levels', + style: { + color: colours.black, + bgcolor: colours.purple, + }, + options: [ + { + type: 'multiselect', + label: 'Levels', + id: 'level', + default: [1], + choices: self.levels, + minSelection: 1, + }, + ], + callback: async (feedback) => { + let l = feedback.options.level.length + let k = self.selected_level.length + + for (let i = 0; i < l; i++) { + let feedback_test = feedback.options.level[i] + for (let j = 0; j < k; j++) { + if (self.selected_level[j].id == feedback_test) { + if (self.selected_level[j].enabled === true) { + // matched + } else { + return false + } + } + } + } + return true + }, + } + + feedbacks['selected_level_dest'] = { + type: 'boolean', + label: 'Selected Levels and Destination', + description: 'Change colour of button on selected levels and destination', + style: { + color: colours.black, + bgcolor:colours.purple, + }, + options: [ + { + type: 'multiselect', + label: 'Levels', + id: 'level', + default: [1], + choices: self.levels, + minSelection: 1, + }, + { + type: 'number', + label: 'Destination', + id: 'dest', + default: 1, + min: 1, + }, + ], + callback: async (feedback) => { + if (self.selected_dest === feedback.options.dest) { + let l = feedback.options.level.length + let k = self.selected_level.length + + for (let i = 0; i < l; i++) { + let feedback_test = feedback.options.level[i] + for (let j = 0; j < k; j++) { + if (self.selected_level[j].id == feedback_test) { + if (self.selected_level[j].enabled === true) { + // matched + } else { + return false + } + } + } + } + return true + } else { + return false + } + }, + } + + feedbacks['selected_dest'] = { + type: 'boolean', + label: 'Selected Destination', + description: 'Change colour of button on selected destination', + style: { + color: colours.black, + bgcolor: colours.green, + }, + options: [ + { + type: 'number', + label: 'Destination', + id: 'dest', + default: 1, + min: 1, + }, + ], + callback: async (feedback) => { + if (self.selected_dest === feedback.options.dest) { + return true + } else { + return false + } + }, + } + + feedbacks['selected_source'] = { + type: 'boolean', + label: 'Selected Source', + description: 'Change colour of button on selected source', + style: { + color: colours.black, + bgcolor: colours.cyan, + }, + options: [ + { + type: 'number', + label: 'Source', + id: 'source', + default: 1, + min: 1, + }, + ], + callback: async (feedback) => { + if (self.selected_source === feedback.options.source) { + return true + } else { + return false + } + }, + } + + feedbacks['source_dest_route'] = { + type: 'boolean', + label: 'Source Routed to Destination', + description: 'Change button colour when this source is routed to selected destination on any level', + style: { + color: colours.black, + bgcolor: colours.orange, + }, + options: [ + { + type: 'number', + label: 'Source', + id: 'source', + default: 1, + min: 1, + }, + ], + callback: async (feedback) => { + // look for this dest in route table + console.log('dest:source feedback ' + self.selected_dest + ':' + feedback.options.source) + for (let i = 0; i < self.routeTable.length; i++) { + if (self.routeTable[i].dest === self.selected_dest) { + if (self.routeTable[i].source === feedback.options.source) { + return true + } + } + } + return false + }, + } + + self.setFeedbackDefinitions(feedbacks) +} diff --git a/index.js b/src/index.js similarity index 92% rename from index.js rename to src/index.js index 8c17b56..dd33904 100644 --- a/index.js +++ b/src/index.js @@ -4,13 +4,80 @@ // // @author Peter Daniel // +// Updated for Companion v3 July 2024, Phillip Ivan Pietruschka + + +import { InstanceBase, runEntrypoint, InstanceStatus } from '@companion-module/base' +import UpgradeScripts from './upgrades.js' +import UpdateActions from './actions.js' +import UpdateFeedbacks from'./feedbacks.js' +import UpdatePresets from './presets.js' +import {SetupVariables, UpdateVariableDefinitions} from './variables.js' +import * as config from './config.js' +import * as crosspoints from './crosspoints.js' +import * as keepalive from './keepalive.js' +import * as labels from './labels.js' +import * as levels from './levels.js' +import * as names from './names.js' +import * as tcp from './tcp.js' +import * as util from './util.js' + + + +class SW_P_08 extends InstanceBase { + constructor(internal) { + super(internal) + Object.assign(this, { ...config, ...crosspoints, ...keepalive, ...labels, ...levels, ...names, ...tcp, ...util }) + } + + async init(config){ + + this.updateStatus(InstanceStatus.Connecting) + this.config = config + + this.updateVariableDefinitions() + this.updateFeedbacks() + this.updateActions() + this.initPresets() + + this.checkFeedbacks('selected_level', 'selected_level_dest','selected_dest','selected_source') + + this.init_tcp() + } + + // When module gets deleted + async destroy() { + this.log('debug', `destroy. ID: ${this.id}`) + if (this.socket) { + this.socket.destroy() + } + this.updateStatus(InstanceStatus.Disconnected) + } + + updateActions() { + UpdateActions(this) + } + + updateFeedbacks() { + UpdateFeedbacks(this) + } + + updatePresets() { + UpdatePresets(this) + } + + updateVariableDefinitions() { + UpdateVariableDefinitions(this) + } + + setupVariables() { + SetupVariables(this) + } +} +runEntrypoint(SW_P_08, UpgradeScripts) -var tcp = require('../../tcp') -var instance_skel = require('../../instance_skel') -var debug -var log -function instance(system) { +/* function instance(system) { var self = this // super-constructor @@ -19,9 +86,9 @@ function instance(system) { self.actions() return self -} +} */ -instance.prototype.updateConfig = function (config) { +/* instance.prototype.updateConfig = function (config) { var self = this console.log('update config') @@ -33,9 +100,9 @@ instance.prototype.updateConfig = function (config) { self.actions() self.init_tcp() -} +} */ -instance.prototype.init = function () { +/* instance.prototype.init = function () { var self = this debug = self.debug @@ -52,9 +119,9 @@ instance.prototype.init = function () { self.checkFeedbacks('selected_source') self.init_tcp() -} +} */ -instance.prototype.destroy = function () { +/* instance.prototype.destroy = function () { // When module gets deleted var self = this @@ -63,9 +130,9 @@ instance.prototype.destroy = function () { } debug('destroy', self.id) -} +} */ -instance.prototype.setupVariables = function () { +/* instance.prototype.setupVariables = function () { var self = this // Implemented Commands @@ -101,9 +168,9 @@ instance.prototype.setupVariables = function () { self.setVariable('Source', self.selected_source) self.setVariable('Destination', self.selected_dest) -} +} */ -instance.prototype.updateVariableDefinitions = function () { +/* instance.prototype.updateVariableDefinitions = function () { var self = this var coreVariables = [] @@ -169,9 +236,9 @@ instance.prototype.updateVariableDefinitions = function () { // console.log(labelDump) self.setVariables(labelDump) -} +} */ -instance.prototype.init_tcp = function () { +/* instance.prototype.init_tcp = function () { var self = this var receivebuffer = Buffer.from('') @@ -325,8 +392,8 @@ instance.prototype.init_tcp = function () { } }) } -} - +} */ +/* instance.prototype.processLabels = function (data) { var self = this var char_length_table = [4, 8, 12] @@ -401,9 +468,9 @@ instance.prototype.extractLabels = function (data, char_length, label_number, la // update dropdown lists self.actions() -} +} */ -instance.prototype.crosspointConnected = function (data) { +/* instance.prototype.crosspointConnected = function (data) { var self = this var matrix = ((data[1] & 0xf0) >> 4) + 1 @@ -472,9 +539,9 @@ instance.prototype.update_crosspoints = function (source, dest, level) { self.routeTable.push(new_route) console.log(self.routeTable) self.checkFeedbacks('source_dest_route') -} +} */ -instance.prototype.config_fields = function () { +/* instance.prototype.config_fields = function () { var self = this return [ @@ -573,9 +640,9 @@ instance.prototype.config_fields = function () { ], }, ] -} +} */ -instance.prototype.setupFeedbacks = function (system) { +/* instance.prototype.setupFeedbacks = function (system) { var self = this // feedback @@ -768,9 +835,9 @@ instance.prototype.feedback = function (feedback, bank) { break } } -} +} */ -instance.prototype.initPresets = function () { +/* instance.prototype.initPresets = function () { var self = this var presets = [] @@ -875,9 +942,9 @@ instance.prototype.initPresets = function () { } self.setPresetDefinitions(presets) -} +} */ -instance.prototype.actions = function () { +/* instance.prototype.actions = function () { var self = this self.system.emit('instance_actions', self.id, { @@ -1191,9 +1258,9 @@ instance.prototype.action = function (action) { if (action.action === 'get_names') { self.readNames() } -} +} */ -instance.prototype.processLevelsSelection = function (selection, state) { +/* instance.prototype.processLevelsSelection = function (selection, state) { var self = this console.log(selection) @@ -1208,9 +1275,9 @@ instance.prototype.processLevelsSelection = function (selection, state) { console.log(self.selected_level) self.checkFeedbacks('selected_level') self.checkFeedbacks('selected_level_dest') -} +} */ -instance.prototype.readNames = function () { +/* instance.prototype.readNames = function () { var self = this // reset @@ -1236,8 +1303,8 @@ instance.prototype.readNames = function () { // get dest names self.sendMessage(get_dest + self.checksum8(get_dest)) } - -instance.prototype.sendAck = function () { + */ +/* instance.prototype.sendAck = function () { var self = this console.log('Sending ACK') @@ -1301,9 +1368,9 @@ instance.prototype.sendMessage = function (message) { self.log('warn', 'Socket not connected') } } -} +} */ -instance.prototype.SetCrosspoint = function (sourceN, destN, levelN) { +/* instance.prototype.SetCrosspoint = function (sourceN, destN, levelN) { var self = this self.log('debug', 'Crosspoint ' + sourceN + '>' + destN + ' level ' + levelN) @@ -1425,8 +1492,8 @@ instance.prototype.getCrosspoints = function (destN) { self.sendMessage(action) } } -} - +} */ +/* instance.prototype.stripNumber = function (str) { var n = str.indexOf(':') if (n > 0) { @@ -1491,7 +1558,8 @@ instance.prototype.checksum8 = function (N) { // console.log('checksum: ' + strResult) return strResult -} - +} */ +/* instance_skel.extendedBy(instance) exports = module.exports = instance + */ \ No newline at end of file diff --git a/src/keepalive.js b/src/keepalive.js new file mode 100644 index 0000000..9d377c3 --- /dev/null +++ b/src/keepalive.js @@ -0,0 +1 @@ +const keepAliveTimer = 30000 \ No newline at end of file diff --git a/src/labels.js b/src/labels.js new file mode 100644 index 0000000..1c0628e --- /dev/null +++ b/src/labels.js @@ -0,0 +1,77 @@ + + +export function processLabels (data) { + let char_length_table = [4, 8, 12] + + // byte1 = matrix (& level for sources) + let char_length = char_length_table[data[2]] + let label_number = 256 * data[3] + data[4] + let labels_in_part = data[5] + let start = 6 + + this.extractLabels(data, char_length, label_number, labels_in_part, start) +} + +export function ext_processSourceLabels (data) { + let char_length_table = [4, 8, 12] + + // byte1 = matrix number + // byte2 = level number + let char_length = char_length_table[data[3]] + let label_number = 256 * data[4] + data[5] + let labels_in_part = data[6] + let start = 7 + + this.extractLabels(data, char_length, label_number, labels_in_part, start) +} + +export function extractLabels (data, char_length, label_number, labels_in_part, s) { + + let l = 0 + + console.log('label chars:' + char_length) + console.log('label number:' + label_number) + console.log('labels in part: ' + labels_in_part) + + while (l < labels_in_part) { + var label = '' + for (var j = 0; j < char_length; j++) { + label = label + String.fromCharCode(data[s + j]) + } + + s = s + char_length + l = l + 1 + label_number = label_number + 1 + + if (data[0] == 0x6a || data[0] == 0xea) { + // sources + this.source_names.splice(label_number - 1, 0, { + id: label_number, + label: label_number.toString() + ': ' + label.trim(), + }) + } else if (data[0] == 0x6b || data[0] == 0xeb) { + // destinations + this.dest_names.splice(label_number - 1, 0, { + id: label_number, + label: label_number.toString() + ': ' + label.trim(), + }) + } + + // console.log('label ' + this.padLeft(label_number,2) + ' |' + label + '|') + // this.log('debug','label ' + this.padLeft(label_number,2) + ' |' + label + '|') + } + + this.setVariableValues({ + Sources: Object.keys(this.source_names).length, + Destinations: Object.keys(this.dest_names).length, + }) + + // need to find a way of only calling these functions on the last part of the labels + this.updateVariableDefinitions() + + console.log(this.source_names) + console.log(this.dest_names) + + // update dropdown lists + this.updateActions() +} \ No newline at end of file diff --git a/src/levels.js b/src/levels.js new file mode 100644 index 0000000..a9b8b8b --- /dev/null +++ b/src/levels.js @@ -0,0 +1,12 @@ +export function processLevelsSelection (selection, state) { + console.log(selection) + selection.forEach((level) => { + if (state === 'toggle') { + this.selected_level[level - 1].enabled = !this.selected_level[level - 1].enabled + } else { + this.selected_level[level - 1].enabled = state + } + }) + console.log(this.selected_level) + this.checkFeedbacks('selected_level', 'selected_level_dest') +} diff --git a/src/names.js b/src/names.js new file mode 100644 index 0000000..68b58c2 --- /dev/null +++ b/src/names.js @@ -0,0 +1,24 @@ +export function readNames() { + // reset + this.source_names = [] + this.dest_names = [] + this.setVariableValues({ Sources: 0, Destinations : 0}) + let get_source + let get_dest + if (this.config.extended_support === true) { + // extended commands (only gets source names for level 1) + var matrix = this.padLeft((this.config.matrix - 1).toString(16), 2) + get_source = 'E4' + matrix + '00' + this.config.name_chars + '04' + get_dest = 'E6' + matrix + this.config.name_chars + '03' + } else { + // standard commands + get_source = '64' + this.config.name_chars + '02' + get_dest = '66' + this.config.name_chars + '02' + } + + // get source names + this.sendMessage(get_source + this.checksum8(get_source)) + + // get dest names + this.sendMessage(get_dest + this.checksum8(get_dest)) +} diff --git a/src/presets.js b/src/presets.js new file mode 100644 index 0000000..17e888a --- /dev/null +++ b/src/presets.js @@ -0,0 +1,110 @@ +import { colours } from './consts.js' + +export async function UpdatePresets(self) { + let presets = [] + + presets.push({ + category: 'Actions', + type: 'button', + name: 'Take', + style: { + style: 'text', + text: 'Take', + size: '18', + color: colours.white, + bgcolor: colours.red, + }, + steps: [{ + down:{ + action: 'take', + }, + }], + }) + + presets.push({ + category: 'Actions', + type: 'button', + name: 'Refresh Names', + style: { + style: 'text', + text: 'Refresh Names', + size: '18', + color: colours.white, + bgcolor: colours.black, + }, + actions: [ + { + action: 'get_names', + }, + ], + }) + + for (let i = 1; i <= 32; i++) { + presets.push({ + category: 'Sources (by number)', + type: 'button', + name: 'Source ' + i, + style: { + style: 'text', + text: 'S' + i, + size: '18', + color: colours.white, + bgcolor: colours.black, + }, + actions: [ + { + action: 'select_source', + options: { + source: i, + }, + }, + ], + feedbacks: [ + { + type: 'selected_source', + options: { + source: i, + }, + style: { + color: colours.black, + bgcolor: colours.cyan, + }, + }, + ], + }) + + presets.push({ + category: 'Destinations (by number)', + type: 'button', + name: 'Destination ' + i, + style: { + style: 'text', + text: 'D' + i, + size: '18', + color: colours.white, + bgcolor: colours.black, + }, + actions: [ + { + action: 'select_dest', + options: { + dest: i, + }, + }, + ], + feedbacks: [ + { + type: 'selected_dest', + options: { + dest: i, + }, + style: { + color: colours.black, + bgcolor: colours.green, + }, + }, + ], + }) + } + self.setPresetDefinitions(presets) +} diff --git a/src/tcp.js b/src/tcp.js new file mode 100644 index 0000000..3aac816 --- /dev/null +++ b/src/tcp.js @@ -0,0 +1,220 @@ +import { InstanceStatus, TCPHelper } from '@companion-module/base' +import { Buffer } from 'node:buffer' +import { DLE, STX, ETX } from './consts.js' +//import { msgDelay } from './consts.js' + +export function sendAck() { + this.log('debug','Sending ACK') + if (this.socket !== undefined && this.socket.connected) { + this.socket.send(this.hexStringToBuffer('1006')) + } else { + this.log('warn','Socket not connected :(') + } +} + +export function sendMessage(message) { + // minimum length is 1 byte + if (message.length < 2) { + this.log('warn', 'Empty or invalid message!') + return + } + + // check that the command is implemented in the router + let cmdCode = parseInt(message.substring(0, 2), 16) + + if (this.config.supported_commands_on_connect === true) { + if (cmdCode !== 97) { + if (this.commands.length > 0) { + if (this.commands.indexOf(cmdCode) !== -1) { + // all good + } else { + this.log('warn', `Command code ${cmdCode} is not implemented by this hardware`) + return + } + } else { + this.log('warn', 'Unable to verify list of implemented commands') + return + } + } + } + + // replace byte value 10 (DLE) in data with 1010 + let packed = '' + for (let j = 0; j < message.length; j = j + 2) { + let b = message.substr(j, 2) + if (b === '10') { + packed = packed + '1010' + } else { + packed = packed + b + } + } + + const cmd = DLE + STX + packed + DLE + ETX + + console.log('Sending >> ' + cmd) + + if (cmd !== undefined) { + if (this.socket !== undefined && this.socket.connected) { + this.socket.send(this.hexStringToBuffer(cmd)) + } else { + this.log('warn', 'Socket not connected') + } + } +} + +export async function init_tcp () { + let receivebuffer = Buffer.from('') + + if (this.socket !== undefined) { + this.socket.destroy() + delete this.socket + } + + if (this.config.host) { + this.socket = new TCPHelper(this.config.host, this.config.port) + + this.socket.on('status_change', function (status, message) { + this.updateStatus(status, message) + }) + + this.socket.on('error', function (err) { + this.log('error', 'Network error: ' + err.message) + this.updateStatus(InstanceStatus.ConnectionFailure, err.message) + }) + + this.socket.on('connect', function () { + this.updateStatus(InstanceStatus.Ok, 'Connected') + + if (this.config.supported_commands_on_connect === true) { + // request protocol implementation + this.sendMessage('61019E') + } + }) + + this.socket.on('data', function (chunk) { + if (Buffer.compare(chunk, receivebuffer) != 0) { + // console.log('Received: ' + chunk.length + ' bytes ', chunk.toString('hex').match(/../g).join(' ')) + // send ACK + this.sendAck() + // Decode + this.socket.emit('decode', chunk) + receivebuffer = chunk + } else { + // duplicate + console.log('Repeated: ' + chunk.length + ' bytes') + } + }) + + this.socket.on('decode', function (data) { + let message = [] + + if (data.length > 0) { + for (let j = 0; j < data.length; j++) { + if (data[j] == 0x10) { + switch (data[j + 1]) { + case 0x02: + console.log('Received SOM') + j++ + continue + //break + + case 0x03: + console.log('Received EOM') + j++ + continue + //break + + case 0x06: + console.log('Received ACK') + j++ + continue + //break + + case 0x10: + // remove repeated byte 0x10 + message.push(data[j]) + j++ + continue + //break + + case 0x15: + console.log('Received NAK') + j++ + continue + //break + + default: + message.push(data[j]) + continue + } + } + message.push(data[j]) + } + } + + if (message.length > 2) { + console.log('message extracted: ' + message) + console.log('Command id: ' + message[0]) + //let requests + //let responses + switch (message[0]) { + // Command + case 0x03: + case 0x04: + // Crosspoint Tally, Crosspoint Connected + this.crosspointConnected(message) + break + + case 0x83: + case 0x84: + // Extended Crosspoint Connected + this.ext_crosspointConnected(message) + break + + case 0x62: + // Protocol Implementation Response + //requests = message[1] + //responses = message[2] + + this.commands = [] + + for (let j = 3; j < message.length - 2; j++) { + this.commands.push(message[j]) + } + + console.log('This router implements: ' + this.commands) + + // request names + if (this.config.read_names_on_connect) { + this.readNames() + } + break + + case 0x6a: + case 0x6b: + // Standard Names Request Reply + this.processLabels(message) + break + + case 0xea: + // Extended Source Names Reply + // Allows for extra Level field in response + this.ext_processSourceLabels(message) + break + + case 0xeb: + // Extended Destination Names Reply + // There is no difference in structure to the standard response + this.processLabels(message) + break + + default: + this.log('warn', 'Unknown response code ' + message[0]) + this.log('debug', message.toString()) + console.log('Unknown response code ' + message[0]) + break + } + } + }) + } +} \ No newline at end of file diff --git a/src/upgrades.js b/src/upgrades.js new file mode 100644 index 0000000..e69de29 diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..e983109 --- /dev/null +++ b/src/util.js @@ -0,0 +1,66 @@ +import { Buffer } from 'node:buffer' + +export function stripNumber(str) { + let n = str.indexOf(':') + if (n > 0) { + return str.slice(n + 2) + } else { + return str + } +} + +export function padLeft(nr, n, str) { + return Array(n - String(nr).length + 1).join(str || '0') + nr +} + +export function asciiToHex(str) { + let arr1 = [] + for (let n = 0, l = str.length; n < l; n++) { + let hex = Number(str.charCodeAt(n)).toString(16) + arr1.push(hex) + } + return arr1.join('') +} + +export function hexStringToBuffer(str) { + return Buffer.from(str, 'hex') +} + +export function getLength(str) { + let length = (str.length / 2).toString(16) + return this.padLeft(length, 4) +} + +export function checksum8(N) { + // convert input value to upper case + let strN = new String(N) + strN = strN.toUpperCase() + + let strHex = new String('0123456789ABCDEF') + let result = 0 + let fctr = 16 + + for (let i = 0; i < strN.length; i++) { + if (strN.charAt(i) == ' ') continue + + let v = strHex.indexOf(strN.charAt(i)) + if (v < 0) { + result = -1 + break + } + + result += v * fctr + + if (fctr == 16) fctr = 1 + else fctr = 16 + } + + // Calculate 2's complement + result = (~(result & 0xff) + 1) & 0xff + + // Convert result to string + const strResult = strHex.charAt(Math.floor(result / 16)) + strHex.charAt(result % 16) + + // console.log('checksum: ' + strResult) + return strResult +} diff --git a/src/variables.js b/src/variables.js new file mode 100644 index 0000000..b14f1fb --- /dev/null +++ b/src/variables.js @@ -0,0 +1,99 @@ +export async function SetupVariables(self) { + // Implemented Commands + self.commands = [] + + // Hold values + self.selected_level = [] + self.selected_dest = 0 + self.selected_source = 0 + + self.routeTable = [] + + self.levels = [] + + self.config.max_levels = self.config.max_levels === undefined ? 3 : self.config.max_levels + + for (var i = 1; i <= self.config.max_levels; i++) { + self.levels.push({ id: i, label: 'Level: ' + i }) + self.selected_level.push({ id: i, enabled: true }) + } + + self.debug(self.levels) + self.debug(self.selected_level) + + // Labels + self.source_names = [] + self.dest_names = [] + + self.updateVariableDefinitions() + + self.setVariable('Sources', 0) + self.setVariable('Destinations', 0) + + self.setVariable('Source', self.selected_source) + self.setVariable('Destination', self.selected_dest) +} + +export async function UpdateVariableDefinition(self) { + let coreVariables = [] + + coreVariables.push( + { + name : 'Number of source names returned by router', + variableId: 'Sources', + }, + { + name : 'Number of destination names returned by router', + variableId: 'Destinations', + }, + { + name : 'Selected destination', + variableId: 'Destination', + }, + { + name : 'Selected source', + variableId: 'Source', + } + ) + + for (let i = 1; i <= self.config.max_levels; i++) { + coreVariables.push({ + name : 'Selected destination source for level ' + i.toString(), + variableId: 'Sel_Dest_Source_Level_' + i.toString(), + }) + coreVariables.push({ + name : 'Selected destination source name for level ' + i.toString(), + variableId: 'Sel_Dest_Source_Name_Level_' + i.toString(), + }) + } + + for (let i = 1; i <= Object.keys(self.source_names).length; i++) { + coreVariables.push({ + label: 'Source ' + i.toString(), + variableId: 'Source_' + i.toString(), + }) + } + + for (let i = 1; i <= Object.keys(self.dest_names).length; i++) { + coreVariables.push({ + label: 'Destination ' + i.toString(), + variableId: 'Destination_' + i.toString(), + }) + } + + self.setVariableDefinitions(coreVariables) + + let labelDump = {} + + for (let i = 0; i < Object.keys(self.source_names).length; i++) { + //let variableValue = self.stripNumber(self.source_names[i].label) not used + labelDump[`Source_${self.source_names[i].id}`] = self.stripNumber(self.source_names[i].label) + } + + for (let i = 0; i < Object.keys(self.dest_names).length; i++) { + labelDump[`Destination_${self.dest_names[i].id}`] = self.stripNumber(self.dest_names[i].label) + } + + // console.log(labelDump) + self.setVariableValues(labelDump) +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..9106d0b --- /dev/null +++ b/yarn.lock @@ -0,0 +1,2219 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@companion-module/base@~1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@companion-module/base/-/base-1.8.0.tgz#1c74204b29451bd7740b2428db372fdfaf51d84d" + integrity sha512-97qmnaPAJZbisYR6EBQ/fZxY8dPLV3my6qIHJzifFl/vkwi+a3ucoXQWLwd5tijM9pqbZOS/3b2V1m6sbdAWlQ== + dependencies: + "@sentry/node" "^7.109.0" + "@sentry/tracing" "^7.109.0" + ajv "^8.12.0" + colord "^2.9.3" + ejson "^2.2.3" + eventemitter3 "^4.0.7" + mimic-fn "^3.0.0" + nanoid "^3.3.4" + p-queue "^6.6.2" + p-timeout "^4.1.0" + tslib "^2.6.2" + +"@companion-module/tools@^1.5.0": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@companion-module/tools/-/tools-1.5.1.tgz#5f5d3e65eee22926e704de3084e3f66de9d841d0" + integrity sha512-9KJC0mZLpg7dlS3MKCYzbUbOjiDMpjjwHgeAxMzh9AOM1ybgsEkmYLfIyZ/EWWSSJ/1s75IJmOtIUWVkZUuqhQ== + dependencies: + "@typescript-eslint/eslint-plugin" "^5.59.9" + "@typescript-eslint/parser" "^5.59.9" + eslint "^8.56.0" + eslint-config-prettier "^8.8.0" + eslint-plugin-n "^16.6.2" + eslint-plugin-prettier "^4.2.1" + find-up "^7.0.0" + parse-author "^2.0.0" + prettier "^2.8.8" + tar "^6.2.1" + webpack "^5.90.0" + webpack-cli "^5.1.4" + zx "^7.2.3" + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@sentry-internal/tracing@7.114.0": + version "7.114.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.114.0.tgz#bdcd364f511e2de45db6e3004faf5685ca2e0f86" + integrity sha512-dOuvfJN7G+3YqLlUY4HIjyWHaRP8vbOgF+OsE5w2l7ZEn1rMAaUbPntAR8AF9GBA6j2zWNoSo8e7GjbJxVofSg== + dependencies: + "@sentry/core" "7.114.0" + "@sentry/types" "7.114.0" + "@sentry/utils" "7.114.0" + +"@sentry-internal/tracing@7.118.0": + version "7.118.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.118.0.tgz#1a96ea745db818e20c2f8273d317f284a416a90a" + integrity sha512-dERAshKlQLrBscHSarhHyUeGsu652bDTUN1FK0m4e3X48M3I5/s+0N880Qjpe5MprNLcINlaIgdQ9jkisvxjfw== + dependencies: + "@sentry/core" "7.118.0" + "@sentry/types" "7.118.0" + "@sentry/utils" "7.118.0" + +"@sentry/core@7.114.0": + version "7.114.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.114.0.tgz#3efe86b92a5379c44dfd0fd4685266b1a30fa898" + integrity sha512-YnanVlmulkjgZiVZ9BfY9k6I082n+C+LbZo52MTvx3FY6RE5iyiPMpaOh67oXEZRWcYQEGm+bKruRxLVP6RlbA== + dependencies: + "@sentry/types" "7.114.0" + "@sentry/utils" "7.114.0" + +"@sentry/core@7.118.0": + version "7.118.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.118.0.tgz#1549b49621bc05a8df16c3546793a299b0638559" + integrity sha512-ol0xBdp3/K11IMAYSQE0FMxBOOH9hMsb/rjxXWe0hfM5c72CqYWL3ol7voPci0GELJ5CZG+9ImEU1V9r6gK64g== + dependencies: + "@sentry/types" "7.118.0" + "@sentry/utils" "7.118.0" + +"@sentry/integrations@7.118.0": + version "7.118.0" + resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.118.0.tgz#f090db621979785c6dc44406da1f72653fa0617c" + integrity sha512-C2rR4NvIMjokF8jP5qzSf1o2zxDx7IeYnr8u15Kb2+HdZtX559owALR0hfgwnfeElqMhGlJBaKUWZ48lXJMzCQ== + dependencies: + "@sentry/core" "7.118.0" + "@sentry/types" "7.118.0" + "@sentry/utils" "7.118.0" + localforage "^1.8.1" + +"@sentry/node@^7.109.0": + version "7.118.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.118.0.tgz#c0b78cabc737eb8a638e82f338fc533455552117" + integrity sha512-79N63DvYKkNPqzmc0cjO+vMZ/nU7+CbE3K3COQNiV7gk58+666G9mRZQJuZVOVebatq5wM5UR0G4LPkwD+J84g== + dependencies: + "@sentry-internal/tracing" "7.118.0" + "@sentry/core" "7.118.0" + "@sentry/integrations" "7.118.0" + "@sentry/types" "7.118.0" + "@sentry/utils" "7.118.0" + +"@sentry/tracing@^7.109.0": + version "7.114.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.114.0.tgz#32a3438b0f2d02fb7b7359fe7712c5a349a2a329" + integrity sha512-eldEYGADReZ4jWdN5u35yxLUSTOvjsiZAYd4KBEpf+Ii65n7g/kYOKAjNl7tHbrEG1EsMW4nDPWStUMk1w+tfg== + dependencies: + "@sentry-internal/tracing" "7.114.0" + +"@sentry/types@7.114.0": + version "7.114.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.114.0.tgz#ab8009d5f6df23b7342121083bed34ee2452e856" + integrity sha512-tsqkkyL3eJtptmPtT0m9W/bPLkU7ILY7nvwpi1hahA5jrM7ppoU0IMaQWAgTD+U3rzFH40IdXNBFb8Gnqcva4w== + +"@sentry/types@7.118.0": + version "7.118.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.118.0.tgz#ca3ab06912f60bc2a7ccf2d2e5ccf43985851aef" + integrity sha512-2drqrD2+6kgeg+W/ycmiti3G4lJrV3hGjY9PpJ3bJeXrh6T2+LxKPzlgSEnKFaeQWkXdZ4eaUbtTXVebMjb5JA== + +"@sentry/utils@7.114.0": + version "7.114.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.114.0.tgz#59d30a79f4acff3c9268de0b345f0bcbc6335112" + integrity sha512-319N90McVpupQ6vws4+tfCy/03AdtsU0MurIE4+W5cubHME08HtiEWlfacvAxX+yuKFhvdsO4K4BB/dj54ideg== + dependencies: + "@sentry/types" "7.114.0" + +"@sentry/utils@7.118.0": + version "7.118.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.118.0.tgz#bfc60826fe3d5d2ae7338ec6ac1f06c20beb179e" + integrity sha512-43qItc/ydxZV1Zb3Kn2M54RwL9XXFa3IAYBO8S82Qvq5YUYmU2AmJ1jgg7DabXlVSWgMA1HntwqnOV3JLaEnTQ== + dependencies: + "@sentry/types" "7.118.0" + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.56.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.10.tgz#eb2370a73bf04a901eeba8f22595c7ee0f7eb58d" + integrity sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/fs-extra@^11.0.1": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== + dependencies: + "@types/jsonfile" "*" + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/jsonfile@*": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== + dependencies: + "@types/node" "*" + +"@types/minimist@^1.2.2": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== + +"@types/node@*": + version "20.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" + integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== + dependencies: + undici-types "~5.26.4" + +"@types/node@^18.16.3": + version "18.19.39" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.39.tgz#c316340a5b4adca3aee9dcbf05de385978590593" + integrity sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ== + dependencies: + undici-types "~5.26.4" + +"@types/ps-tree@^1.1.2": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@types/ps-tree/-/ps-tree-1.1.6.tgz#fbb22dabe3d64b79295f37ce0afb7320a26ac9a6" + integrity sha512-PtrlVaOaI44/3pl3cvnlK+GxOM3re2526TJvPvh7W+keHIXdV4TE0ylpPBAcvFQCbGitaTXwL9u+RF7qtVeazQ== + +"@types/semver@^7.3.12": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== + +"@types/which@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/which/-/which-3.0.4.tgz#2c3a89be70c56a84a6957a7264639f39ae4340a1" + integrity sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w== + +"@typescript-eslint/eslint-plugin@^5.59.9": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.59.9": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== + dependencies: + "@webassemblyjs/ast" "1.12.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.12.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.16.0.tgz#22e2a92b94f005f7e0f9c9d39652ef0b8f6f0cb4" + integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +author-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/author-regex/-/author-regex-1.0.0.tgz#d08885be6b9bbf9439fe087c76287245f0a81450" + integrity sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.21.10: + version "4.23.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96" + integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== + dependencies: + caniuse-lite "^1.0.30001629" + electron-to-chromium "^1.4.796" + node-releases "^2.0.14" + update-browserslist-db "^1.0.16" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + +builtins@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.1.0.tgz#6d85eeb360c4ebc166c3fdef922a15aa7316a5e8" + integrity sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg== + dependencies: + semver "^7.0.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001629: + version "1.0.30001640" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz#32c467d4bf1f1a0faa63fc793c2ba81169e7652f" + integrity sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +ejson@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/ejson/-/ejson-2.2.3.tgz#2b18c2d8f5d61a5cfc6e3eab72c3343909e7afd2" + integrity sha512-hsFvJp6OpGxFRQfBR3PSxFpaPALdHDY+SB3TRbMpLWNhvu8GzLiZutof5+/DFd2QekZo3KyXau75ngdJqQUSrw== + +electron-to-chromium@^1.4.796: + version "1.4.816" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz#3624649d1e7fde5cdbadf59d31a524245d8ee85f" + integrity sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw== + +enhanced-resolve@^5.17.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" + integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.13.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" + integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== + +es-module-lexer@^1.2.1: + version "1.5.4" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.4.tgz#a8efec3a3da991e60efa6b633a7cad6ab8d26b78" + integrity sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw== + +escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-compat-utils@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz#7fc92b776d185a70c4070d03fd26fde3d59652e4" + integrity sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q== + dependencies: + semver "^7.5.4" + +eslint-config-prettier@^8.8.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== + +eslint-plugin-es-x@^7.5.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" + integrity sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ== + dependencies: + "@eslint-community/eslint-utils" "^4.1.2" + "@eslint-community/regexpp" "^4.11.0" + eslint-compat-utils "^0.5.1" + +eslint-plugin-n@^16.6.2: + version "16.6.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz#6a60a1a376870064c906742272074d5d0b412b0b" + integrity sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + builtins "^5.0.1" + eslint-plugin-es-x "^7.5.0" + get-tsconfig "^4.7.0" + globals "^13.24.0" + ignore "^5.2.4" + is-builtin-module "^3.2.1" + is-core-module "^2.12.1" + minimatch "^3.1.2" + resolve "^1.22.2" + semver "^7.5.3" + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.56.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter3@^4.0.4, eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.9, fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fetch-blob@^3.1.2, fetch-blob@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== + dependencies: + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-7.0.0.tgz#e8dec1455f74f78d888ad65bf7ca13dd2b4e66fb" + integrity sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g== + dependencies: + locate-path "^7.2.0" + path-exists "^5.0.0" + unicorn-magic "^0.1.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== + dependencies: + fetch-blob "^3.1.2" + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + +fs-extra@^11.1.1: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +fx@*: + version "35.0.0" + resolved "https://registry.yarnpkg.com/fx/-/fx-35.0.0.tgz#44787ce7ed997e4caca313119ec8584e8413ed9d" + integrity sha512-O07q+Lknrom5RUX/u53tjo2KTTLUnL0K703JbqMYb19ORijfJNvijzFqqYXEjdk25T9R14S6t6wHD8fCWXCM0g== + +get-tsconfig@^4.7.0: + version "4.7.5" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" + integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== + dependencies: + resolve-pkg-maps "^1.0.0" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0, globals@^13.24.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^13.1.4: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +ignore@^5.2.0, ignore@^5.2.4: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + +is-core-module@^2.12.1, is-core-module@^2.13.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" + integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== + dependencies: + hasown "^2.0.2" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lie@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== + dependencies: + immediate "~3.0.5" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +localforage@^1.8.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" + integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== + dependencies: + lie "3.1.1" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +locate-path@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +nanoid@^3.3.4: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e" + integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" + +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + +p-timeout@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-4.1.0.tgz#788253c0452ab0ffecf18a62dff94ff1bd09ca0a" + integrity sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-author@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-author/-/parse-author-2.0.0.tgz#d3460bf1ddd0dfaeed42da754242e65fb684a81f" + integrity sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw== + dependencies: + author-regex "^1.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +ps-tree@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve@^1.20.0, resolve@^1.22.2: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver@^7.0.0, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== + dependencies: + duplexer "~0.1.1" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.31.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.1.tgz#735de3c987dd671e95190e6b98cfe2f07f3cf0d4" + integrity sha512-37upzU1+viGvuFtBo9NPufCb9dwM0+l9hMxYyWfBA+fbwrPqNJAhbZ6W47bBFnZHKHTUBnMvi87434qq+qnxOg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through@2, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +update-browserslist-db@^1.0.16: + version "1.1.0" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2, uri-js@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +watchpack@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +web-streams-polyfill@^3.0.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== + +webpack-cli@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.90.0: + version "5.92.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.92.1.tgz#eca5c1725b9e189cffbd86e8b6c3c7400efc5788" + integrity sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" + acorn "^8.7.1" + acorn-import-attributes "^1.9.5" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" + webpack-sources "^3.2.3" + +webpod@^0: + version "0.0.2" + resolved "https://registry.yarnpkg.com/webpod/-/webpod-0.0.2.tgz#b577c93604fd23596488735887168b3236e3adae" + integrity sha512-cSwwQIeg8v4i3p4ajHhwgR7N6VyxAf+KYSSsY6Pd3aETE+xEU4vbitz7qQkB0I321xnhDdgtxuiSfk5r/FVtjg== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +which@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" + integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^2.2.2: + version "2.4.5" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e" + integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" + integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== + +zx@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/zx/-/zx-7.2.3.tgz#d9fef6bd084f7e21994080de09fb20e441074c39" + integrity sha512-QODu38nLlYXg/B/Gw7ZKiZrvPkEsjPN3LQ5JFXM7h0JvwhEdPNNl+4Ao1y4+o3CLNiDUNcwzQYZ4/Ko7kKzCMA== + dependencies: + "@types/fs-extra" "^11.0.1" + "@types/minimist" "^1.2.2" + "@types/node" "^18.16.3" + "@types/ps-tree" "^1.1.2" + "@types/which" "^3.0.0" + chalk "^5.2.0" + fs-extra "^11.1.1" + fx "*" + globby "^13.1.4" + minimist "^1.2.8" + node-fetch "3.3.1" + ps-tree "^1.2.0" + webpod "^0" + which "^3.0.0" + yaml "^2.2.2" From e9ff7eb1dd15255d86d1f5049d760bb036857074 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Fri, 5 Jul 2024 18:21:27 +1000 Subject: [PATCH 02/25] implement keepalive framework (still requires KA message) remove old code from index.js --- src/index.js | 1492 +--------------------------------------------- src/keepalive.js | 22 +- src/tcp.js | 4 +- 3 files changed, 26 insertions(+), 1492 deletions(-) diff --git a/src/index.js b/src/index.js index dd33904..7f5bc5a 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,6 @@ // // Updated for Companion v3 July 2024, Phillip Ivan Pietruschka - import { InstanceBase, runEntrypoint, InstanceStatus } from '@companion-module/base' import UpgradeScripts from './upgrades.js' import UpdateActions from './actions.js' @@ -48,6 +47,7 @@ class SW_P_08 extends InstanceBase { // When module gets deleted async destroy() { this.log('debug', `destroy. ID: ${this.id}`) + this.stopKeepAliveTimer() if (this.socket) { this.socket.destroy() } @@ -74,1492 +74,4 @@ class SW_P_08 extends InstanceBase { SetupVariables(this) } } -runEntrypoint(SW_P_08, UpgradeScripts) - - -/* function instance(system) { - var self = this - - // super-constructor - instance_skel.apply(this, arguments) - - self.actions() - - return self -} */ - -/* instance.prototype.updateConfig = function (config) { - var self = this - - console.log('update config') - - self.config = config - - self.setupVariables() - self.setupFeedbacks() - self.actions() - - self.init_tcp() -} */ - -/* instance.prototype.init = function () { - var self = this - - debug = self.debug - log = self.log - - self.setupVariables() - self.setupFeedbacks() - self.actions() - self.initPresets() - - self.checkFeedbacks('selected_level') - self.checkFeedbacks('selected_level_dest') - self.checkFeedbacks('selected_dest') - self.checkFeedbacks('selected_source') - - self.init_tcp() -} */ - -/* instance.prototype.destroy = function () { - // When module gets deleted - var self = this - - if (self.socket !== undefined) { - self.socket.destroy() - } - - debug('destroy', self.id) -} */ - -/* instance.prototype.setupVariables = function () { - var self = this - - // Implemented Commands - self.commands = [] - - // Hold values - self.selected_level = [] - self.selected_dest = 0 - self.selected_source = 0 - - self.routeTable = [] - - self.levels = [] - - self.config.max_levels = self.config.max_levels === undefined ? 3 : self.config.max_levels - - for (var i = 1; i <= self.config.max_levels; i++) { - self.levels.push({ id: i, label: 'Level: ' + i }) - self.selected_level.push({ id: i, enabled: true }) - } - - self.debug(self.levels) - self.debug(self.selected_level) - - // Labels - self.source_names = [] - self.dest_names = [] - - self.updateVariableDefinitions() - - self.setVariable('Sources', 0) - self.setVariable('Destinations', 0) - - self.setVariable('Source', self.selected_source) - self.setVariable('Destination', self.selected_dest) -} */ - -/* instance.prototype.updateVariableDefinitions = function () { - var self = this - var coreVariables = [] - - coreVariables.push( - { - label: 'Number of source names returned by router', - name: 'Sources', - }, - { - label: 'Number of destination names returned by router', - name: 'Destinations', - }, - { - label: 'Selected destination', - name: 'Destination', - }, - { - label: 'Selected source', - name: 'Source', - } - ) - - for (var i = 1; i <= self.config.max_levels; i++) { - coreVariables.push({ - label: 'Selected destination source for level ' + i.toString(), - name: 'Sel_Dest_Source_Level_' + i.toString(), - }) - coreVariables.push({ - label: 'Selected destination source name for level ' + i.toString(), - name: 'Sel_Dest_Source_Name_Level_' + i.toString(), - }) - } - - for (var i = 1; i <= Object.keys(self.source_names).length; i++) { - coreVariables.push({ - label: 'Source ' + i.toString(), - name: 'Source_' + i.toString(), - }) - } - - for (var i = 1; i <= Object.keys(self.dest_names).length; i++) { - coreVariables.push({ - label: 'Destination ' + i.toString(), - name: 'Destination_' + i.toString(), - }) - } - - self.setVariableDefinitions(coreVariables) - - var labelDump = {} - - for (var i = 0; i < Object.keys(self.source_names).length; i++) { - var variableName = 'Source_' + self.source_names[i].id - var variableValue = self.stripNumber(self.source_names[i].label) - labelDump[variableName] = variableValue - } - - for (var i = 0; i < Object.keys(self.dest_names).length; i++) { - var variableName = 'Destination_' + self.dest_names[i].id - var variableValue = self.stripNumber(self.dest_names[i].label) - labelDump[variableName] = variableValue - } - - // console.log(labelDump) - self.setVariables(labelDump) -} */ - -/* instance.prototype.init_tcp = function () { - var self = this - var receivebuffer = Buffer.from('') - - if (self.socket !== undefined) { - self.socket.destroy() - delete self.socket - } - - if (self.config.host) { - self.socket = new tcp(self.config.host, self.config.port) - - self.socket.on('status_change', function (status, message) { - self.status(status, message) - }) - - self.socket.on('error', function (err) { - debug('Network error', err) - self.log('error', 'Network error: ' + err.message) - }) - - self.socket.on('connect', function () { - debug('Connected') - - if (self.config.supported_commands_on_connect === true) { - // request protocol implementation - self.sendMessage('61019E') - } - }) - - self.socket.on('data', function (chunk) { - if (Buffer.compare(chunk, receivebuffer) != 0) { - // console.log('Received: ' + chunk.length + ' bytes ', chunk.toString('hex').match(/../g).join(' ')) - // send ACK - self.sendAck() - // Decode - self.socket.emit('decode', chunk) - receivebuffer = chunk - } else { - // duplicate - console.log('Repeated: ' + chunk.length + ' bytes') - } - }) - - self.socket.on('decode', function (data) { - var message = [] - - if (data.length > 0) { - for (var j = 0; j < data.length; j++) { - if (data[j] == 0x10) { - switch (data[j + 1]) { - case 0x02: - console.log('Received SOM') - j++ - continue - break - - case 0x03: - console.log('Received EOM') - j++ - continue - break - - case 0x06: - console.log('Received ACK') - j++ - continue - break - - case 0x10: - // remove repeated byte 0x10 - message.push(data[j]) - j++ - continue - break - - case 0x15: - console.log('Received NAK') - j++ - continue - break - - default: - message.push(data[j]) - continue - } - } - message.push(data[j]) - } - } - - if (message.length > 2) { - console.log('message extracted: ' + message) - console.log('Command id: ' + message[0]) - switch (message[0]) { - // Command - case 0x03: - case 0x04: - // Crosspoint Tally, Crosspoint Connected - self.crosspointConnected(message) - break - - case 0x83: - case 0x84: - // Extended Crosspoint Connected - self.ext_crosspointConnected(message) - break - - case 0x62: - // Protocol Implementation Response - var requests = message[1] - var responses = message[2] - - self.commands = [] - - for (var j = 3; j < message.length - 2; j++) { - self.commands.push(message[j]) - } - - console.log('This router implements: ' + self.commands) - - // request names - if (self.config.read_names_on_connect) { - self.readNames() - } - break - - case 0x6a: - case 0x6b: - // Standard Names Request Reply - self.processLabels(message) - break - - case 0xea: - // Extended Source Names Reply - // Allows for extra Level field in response - self.ext_processSourceLabels(message) - break - - case 0xeb: - // Extended Destination Names Reply - // There is no difference in structure to the standard response - self.processLabels(message) - break - - default: - self.log('warn', 'Unknown response code ' + message[0]) - self.log('debug', message.toString()) - console.log('Unknown response code ' + message[0]) - break - } - } - }) - } -} */ -/* -instance.prototype.processLabels = function (data) { - var self = this - var char_length_table = [4, 8, 12] - - // byte1 = matrix (& level for sources) - var char_length = char_length_table[data[2]] - var label_number = 256 * data[3] + data[4] - var labels_in_part = data[5] - var start = 6 - - self.extractLabels(data, char_length, label_number, labels_in_part, start) -} - -instance.prototype.ext_processSourceLabels = function (data) { - var self = this - var char_length_table = [4, 8, 12] - - // byte1 = matrix number - // byte2 = level number - var char_length = char_length_table[data[3]] - var label_number = 256 * data[4] + data[5] - var labels_in_part = data[6] - var start = 7 - - self.extractLabels(data, char_length, label_number, labels_in_part, start) -} - -instance.prototype.extractLabels = function (data, char_length, label_number, labels_in_part, s) { - var self = this - var l = 0 - - console.log('label chars:' + char_length) - console.log('label number:' + label_number) - console.log('labels in part: ' + labels_in_part) - - while (l < labels_in_part) { - var label = '' - for (var j = 0; j < char_length; j++) { - label = label + String.fromCharCode(data[s + j]) - } - - s = s + char_length - l = l + 1 - label_number = label_number + 1 - - if (data[0] == 0x6a || data[0] == 0xea) { - // sources - self.source_names.splice(label_number - 1, 0, { - id: label_number, - label: label_number.toString() + ': ' + label.trim(), - }) - } else if (data[0] == 0x6b || data[0] == 0xeb) { - // destinations - self.dest_names.splice(label_number - 1, 0, { - id: label_number, - label: label_number.toString() + ': ' + label.trim(), - }) - } - - // console.log('label ' + self.padLeft(label_number,2) + ' |' + label + '|') - // self.log('debug','label ' + self.padLeft(label_number,2) + ' |' + label + '|') - } - - self.setVariable('Sources', Object.keys(self.source_names).length) - self.setVariable('Destinations', Object.keys(self.dest_names).length) - - // need to find a way of only calling these functions on the last part of the labels - self.updateVariableDefinitions() - - console.log(self.source_names) - console.log(self.dest_names) - - // update dropdown lists - self.actions() -} */ - -/* instance.prototype.crosspointConnected = function (data) { - var self = this - - var matrix = ((data[1] & 0xf0) >> 4) + 1 - var level = (data[1] & 0x0f) + 1 - var destDiv = (data[2] & 0x70) >> 4 - var sourceDiv = data[2] & 0x7 - var dest = 128 * destDiv + data[3] + 1 - var source = 128 * sourceDiv + data[4] + 1 - - console.log('Source ' + source + ' routed to ' + dest + ' on level ' + level) - self.log('debug', 'Source ' + source + ' routed to destination ' + dest + ' on level ' + level) - - self.update_crosspoints(source, dest, level) -} - -instance.prototype.ext_crosspointConnected = function (data) { - var self = this - - var matrix = data[1] + 1 - var level = data[2] + 1 - var destDiv = data[3] * 256 - var destMod = data[4] - var sourceDiv = data[5] * 256 - var sourceMod = data[6] - var dest = destDiv + destMod + 1 - var source = sourceDiv + sourceMod + 1 - - console.log('Source ' + source + ' routed to ' + dest + ' on level ' + level) - self.log('debug', 'Source ' + source + ' routed to destination ' + dest + ' on level ' + level) - - self.update_crosspoints(source, dest, level) -} - -instance.prototype.update_crosspoints = function (source, dest, level) { - var self = this - - if (dest == self.selected_dest) { - // update variables for selected dest source - self.setVariable('Sel_Dest_Source_Level_' + level.toString(), source) - if (self.source_names.length > 0) { - // only if names have been retrieved - try { - self.setVariable( - 'Sel_Dest_Source_Name_Level_' + level.toString(), - self.stripNumber(self.source_names[source - 1].label) - ) - } catch (e) { - self.log('debug', 'Unable to set Sel_Dest_Source_Name_Level') - } - } - } - - // store route data - for (var i = 0; i < self.routeTable.length; i++) { - if (self.routeTable[i].level === level && self.routeTable[i].dest === dest) { - // update existing - self.routeTable[i].source = source - console.log(self.routeTable) - self.checkFeedbacks('source_dest_route') - return - } - } - - // add new - var new_route = { level: level, dest: dest, source: source } - self.routeTable.push(new_route) - console.log(self.routeTable) - self.checkFeedbacks('source_dest_route') -} */ - -/* instance.prototype.config_fields = function () { - var self = this - - return [ - { - type: 'text', - id: 'info', - width: 12, - label: 'Information', - value: 'This module will allow you to control broadcast routers which implement the SW-P-08 standard protocol.', - }, - { - type: 'textinput', - id: 'host', - label: 'Device IP', - width: 6, - regex: self.REGEX_IP, - }, - { - type: 'textinput', - id: 'port', - label: 'Device Port', - width: 6, - default: '8910', - regex: self.REGEX_PORT, - }, - { - type: 'number', - label: 'Matrix Number (Default 1)', - id: 'matrix', - width: 6, - default: 1, - min: 1, - max: 16, - }, - { - type: 'number', - label: 'Number of levels defined in router', - id: 'max_levels', - width: 6, - default: 3, - min: 1, - max: 256, - }, - { - type: 'checkbox', - label: 'Enable', - id: 'supported_commands_on_connect', - width: 1, - default: true, - }, - { - type: 'text', - label: 'Request supported commands on connection', - id: 'supported_commands_on_connect_txt', - value: 'Not supported by all router controllers. Try disabling this feature if you encounter problems', - width: 11, - }, - { - type: 'checkbox', - label: 'Enable', - id: 'read_names_on_connect', - width: 1, - default: false, - }, - { - type: 'text', - label: 'Request names on connection', - id: 'read_names_on_connect_txt', - value: 'Not supported by all router controllers', - width: 11, - }, - { - type: 'checkbox', - label: 'Enable', - id: 'extended_support', - width: 1, - default: false, - }, - { - type: 'text', - label: 'Router has more than 1024 source or destination names', - id: 'extended_support_txt', - value: 'Use extended command set for name requests. Not supported by all router controllers', - width: 11, - }, - { - type: 'dropdown', - label: 'Request name length (ignored by some routers)', - id: 'name_chars', - width: 6, - default: '01', - choices: [ - { id: '00', label: '4 characters' }, - { id: '01', label: '8 characters' }, - { id: '02', label: '12 characters' }, - ], - }, - ] -} */ - -/* instance.prototype.setupFeedbacks = function (system) { - var self = this - - // feedback - var feedbacks = {} - - feedbacks['selected_level'] = { - type: 'boolean', - label: 'Selected Levels', - description: 'Change colour of button on selected levels', - style: { - color: self.rgb(0, 0, 0), - bgcolor: self.rgb(255, 102, 255), - }, - options: [ - { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], - choices: self.levels, - minSelection: 1, - }, - ], - } - - feedbacks['selected_level_dest'] = { - type: 'boolean', - label: 'Selected Levels and Destination', - description: 'Change colour of button on selected levels and destination', - style: { - color: self.rgb(0, 0, 0), - bgcolor: self.rgb(255, 102, 255), - }, - options: [ - { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], - choices: self.levels, - minSelection: 1, - }, - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, - }, - ], - } - - feedbacks['selected_dest'] = { - type: 'boolean', - label: 'Selected Destination', - description: 'Change colour of button on selected destination', - style: { - color: self.rgb(0, 0, 0), - bgcolor: self.rgb(102, 255, 102), - }, - options: [ - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, - }, - ], - } - - feedbacks['selected_source'] = { - type: 'boolean', - label: 'Selected Source', - description: 'Change colour of button on selected source', - style: { - color: self.rgb(0, 0, 0), - bgcolor: self.rgb(102, 255, 255), - }, - options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - }, - ], - } - - feedbacks['source_dest_route'] = { - type: 'boolean', - label: 'Source Routed to Destination', - description: 'Change button colour when this source is routed to selected destination on any level', - style: { - color: self.rgb(0, 0, 0), - bgcolor: self.rgb(255, 191, 128), - }, - options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - }, - ], - } - - self.setFeedbackDefinitions(feedbacks) -} - -instance.prototype.feedback = function (feedback, bank) { - var self = this - - switch (feedback.type) { - case 'selected_level': { - var l = feedback.options.level.length - var k = self.selected_level.length - - for (var i = 0; i < l; i++) { - var feedback_test = feedback.options.level[i] - for (var j = 0; j < k; j++) { - if (self.selected_level[j].id == feedback_test) { - if (self.selected_level[j].enabled === true) { - // matched - } else { - return false - } - } - } - } - return true - break - } - - case 'selected_level_dest': { - if (self.selected_dest === feedback.options.dest) { - var l = feedback.options.level.length - var k = self.selected_level.length - - for (var i = 0; i < l; i++) { - var feedback_test = feedback.options.level[i] - for (var j = 0; j < k; j++) { - if (self.selected_level[j].id == feedback_test) { - if (self.selected_level[j].enabled === true) { - // matched - } else { - return false - } - } - } - } - return true - } else { - return false - } - break - } - - case 'selected_dest': { - if (self.selected_dest === feedback.options.dest) { - return true - } else { - return false - } - break - } - - case 'selected_source': { - if (self.selected_source === feedback.options.source) { - return true - } else { - return false - } - break - } - - case 'source_dest_route': { - // look for this dest in route table - console.log('dest:source feedback ' + self.selected_dest + ':' + feedback.options.source) - for (var i = 0; i < self.routeTable.length; i++) { - if (self.routeTable[i].dest === self.selected_dest) { - if (self.routeTable[i].source === feedback.options.source) { - return true - } - } - } - return false - break - } - } -} */ - -/* instance.prototype.initPresets = function () { - var self = this - var presets = [] - - presets.push({ - category: 'Actions', - label: 'Take', - bank: { - style: 'text', - text: 'Take', - size: '18', - color: self.rgb(255, 255, 255), - bgcolor: self.rgb(240, 0, 0), - }, - actions: [ - { - action: 'take', - }, - ], - }) - - presets.push({ - category: 'Actions', - label: 'Refresh Names', - bank: { - style: 'text', - text: 'Refresh Names', - size: '18', - color: self.rgb(255, 255, 255), - bgcolor: self.rgb(0, 0, 0), - }, - actions: [ - { - action: 'get_names', - }, - ], - }) - - for (var i = 1; i <= 32; i++) { - presets.push({ - category: 'Sources (by number)', - label: 'Source ' + i, - bank: { - style: 'text', - text: 'S' + i, - size: '18', - color: self.rgb(255, 255, 255), - bgcolor: self.rgb(0, 0, 0), - }, - actions: [ - { - action: 'select_source', - options: { - source: i, - }, - }, - ], - feedbacks: [ - { - type: 'selected_source', - options: { - source: i, - }, - style: { - color: self.rgb(0, 0, 0), - bgcolor: self.rgb(102, 255, 255), - }, - }, - ], - }) - - presets.push({ - category: 'Destinations (by number)', - label: 'Destination ' + i, - bank: { - style: 'text', - text: 'D' + i, - size: '18', - color: self.rgb(255, 255, 255), - bgcolor: self.rgb(0, 0, 0), - }, - actions: [ - { - action: 'select_dest', - options: { - dest: i, - }, - }, - ], - feedbacks: [ - { - type: 'selected_dest', - options: { - dest: i, - }, - style: { - color: self.rgb(0, 0, 0), - bgcolor: self.rgb(102, 255, 102), - }, - }, - ], - }) - } - - self.setPresetDefinitions(presets) -} */ - -/* instance.prototype.actions = function () { - var self = this - - self.system.emit('instance_actions', self.id, { - select_level: { - label: 'Select Levels', - options: [ - { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], - choices: self.levels, - minSelection: 1, - }, - ], - }, - - deselect_level: { - label: 'De-Select Levels', - options: [ - { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], - choices: self.levels, - minSelection: 1, - }, - ], - }, - - toggle_level: { - label: 'Toggle Levels', - options: [ - { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], - choices: self.levels, - minSelection: 1, - }, - ], - }, - - select_dest: { - label: 'Select Destination', - options: [ - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, - max: 65536, - }, - ], - }, - - select_dest_name: { - label: 'Select Destination name', - options: [ - { - type: 'dropdown', - label: 'Destination', - id: 'dest', - default: 1, - choices: self.dest_names, - }, - ], - }, - - select_source: { - label: 'Select Source', - options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - max: 65536, - }, - ], - }, - - select_source_name: { - label: 'Select Source name', - options: [ - { - type: 'dropdown', - label: 'Source', - id: 'source', - default: 1, - choices: self.source_names, - }, - ], - }, - - route_source: { - label: 'Route Source to selected Levels and Destination', - options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - max: 65536, - }, - ], - }, - - route_source_name: { - label: 'Route Source name to selected Levels and Destination', - options: [ - { - type: 'dropdown', - label: 'Source', - id: 'source', - default: 1, - choices: self.source_names, - }, - ], - }, - - take: { - label: 'Take', - }, - - clear: { - label: 'Clear', - options: [ - { - type: 'dropdown', - label: 'Clear', - id: 'clear', - default: 'all', - choices: [ - { id: 'all', label: 'All' }, - { id: 'level', label: 'Levels' }, - { id: 'dest', label: 'Destination' }, - { id: 'source', label: 'Source' }, - ], - }, - { - type: 'checkbox', - label: "Enable all levels on 'Clear All' or 'Clear Levels'", - id: 'clear_enable_levels', - default: true, - }, - ], - }, - - set_crosspoint: { - label: 'Set crosspoint', - options: [ - { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], - choices: self.levels, - minSelection: 1, - }, - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - max: 65536, - }, - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, - max: 65536, - }, - ], - }, - - set_crosspoint_name: { - label: 'Set crosspoint by name', - options: [ - { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], - choices: self.levels, - minSelection: 1, - }, - { - type: 'dropdown', - label: 'Source', - id: 'source', - default: 1, - choices: self.source_names, - }, - { - type: 'dropdown', - label: 'Destination', - id: 'dest', - default: 1, - choices: self.dest_names, - }, - ], - }, - - get_names: { - label: 'Refresh Source and Destination names', - }, - }) -} - -instance.prototype.action = function (action) { - var self = this - - const opt = action.options - - if (action.action === 'select_level') { - self.processLevelsSelection(opt.level, true) - return - } - - if (action.action === 'deselect_level') { - self.processLevelsSelection(opt.level, false) - return - } - - if (action.action === 'toggle_level') { - self.processLevelsSelection(opt.level, 'toggle') - return - } - - if (action.action === 'select_dest' || action.action === 'select_dest_name') { - self.selected_dest = parseInt(opt.dest) - self.getCrosspoints(opt.dest) - console.log('set destination ' + self.selected_dest) - self.setVariable('Destination', self.selected_dest) - self.checkFeedbacks('selected_dest') - self.checkFeedbacks('selected_level_dest') - return - } - - if (action.action === 'select_source' || action.action === 'select_source_name') { - self.selected_source = parseInt(opt.source) - console.log('set source ' + self.selected_source) - self.setVariable('Source', self.selected_source) - self.checkFeedbacks('selected_source') - return - } - - if (action.action === 'route_source' || action.action === 'route_source_name') { - console.log(self.selected_level) - var l = self.selected_level.length - for (var i = 0; i < l; i++) { - if (self.selected_level[i].enabled === true) { - self.SetCrosspoint(opt.source, self.selected_dest, self.selected_level[i].id) - } - } - } - - if (action.action === 'take') { - console.log(self.selected_level) - var l = self.selected_level.length - for (var i = 0; i < l; i++) { - if (self.selected_level[i].enabled === true) { - self.SetCrosspoint(self.selected_source, self.selected_dest, self.selected_level[i].id) - } - } - } - - if (action.action === 'set_crosspoint' || action.action === 'set_crosspoint_name') { - for (let level_val of opt.level) { - self.SetCrosspoint(opt.source, opt.dest, level_val) - } - } - - if (action.action === 'clear') { - if (opt.clear === 'all' || opt.clear === 'level') { - self.selected_level = [] - for (var i = 1; i <= self.config.max_levels; i++) { - self.selected_level.push({ id: i, enabled: opt.clear_enable_levels }) - } - self.checkFeedbacks('selected_level') - self.checkFeedbacks('selected_level_dest') - console.log('clear levels') - console.log(self.selected_level) - } - - if (opt.clear === 'all' || opt.clear === 'dest') { - self.selected_dest = 0 - self.setVariable('Destination', self.selected_dest) - self.checkFeedbacks('selected_dest') - self.checkFeedbacks('selected_level_dest') - console.log('clear dest') - } - - if (opt.clear === 'all' || opt.clear === 'source') { - self.selected_source = 0 - self.setVariable('Source', self.selected_source) - self.checkFeedbacks('selected_source') - console.log('clear source') - } - } - - if (action.action === 'get_names') { - self.readNames() - } -} */ - -/* instance.prototype.processLevelsSelection = function (selection, state) { - var self = this - - console.log(selection) - selection.forEach((level) => { - if (state === 'toggle') { - self.selected_level[level - 1].enabled = !self.selected_level[level - 1].enabled - } else { - self.selected_level[level - 1].enabled = state - } - }) - - console.log(self.selected_level) - self.checkFeedbacks('selected_level') - self.checkFeedbacks('selected_level_dest') -} */ - -/* instance.prototype.readNames = function () { - var self = this - - // reset - self.source_names = [] - self.dest_names = [] - self.setVariable('Sources', 0) - self.setVariable('Destinations', 0) - - if (self.config.extended_support === true) { - // extended commands (only gets source names for level 1) - var matrix = self.padLeft((self.config.matrix - 1).toString(16), 2) - get_source = 'E4' + matrix + '00' + self.config.name_chars + '04' - get_dest = 'E6' + matrix + self.config.name_chars + '03' - } else { - // standard commands - get_source = '64' + self.config.name_chars + '02' - get_dest = '66' + self.config.name_chars + '02' - } - - // get source names - self.sendMessage(get_source + self.checksum8(get_source)) - - // get dest names - self.sendMessage(get_dest + self.checksum8(get_dest)) -} - */ -/* instance.prototype.sendAck = function () { - var self = this - - console.log('Sending ACK') - if (self.socket !== undefined && self.socket.connected) { - self.socket.send(self.hexStringToBuffer('1006')) - } else { - debug('Socket not connected :(') - } -} - -instance.prototype.sendMessage = function (message) { - var self = this - const DLE = '10' - const STX = '02' - const ETX = '03' - - // minimum length is 1 byte - if (message.length < 2) { - self.log('warn', 'Empty or invalid message!') - return - } - - // check that the command is implemented in the router - var cmdCode = parseInt(message.substring(0, 2), 16) - - if (self.config.supported_commands_on_connect === true) { - if (cmdCode !== 97) { - if (self.commands.length > 0) { - if (self.commands.indexOf(cmdCode) !== -1) { - // all good - } else { - self.log('warn', 'Command code ' + cmdCode + ' is not implemented by this hardware') - return - } - } else { - self.log('warn', 'Unable to verify list of implemented commands') - return - } - } - } - - // replace byte value 10 (DLE) in data with 1010 - var packed = '' - for (var j = 0; j < message.length; j = j + 2) { - var b = message.substr(j, 2) - if (b === '10') { - packed = packed + '1010' - } else { - packed = packed + b - } - } - - cmd = DLE + STX + packed + DLE + ETX - - console.log('Sending >> ' + cmd) - - if (cmd !== undefined) { - if (self.socket !== undefined && self.socket.connected) { - self.socket.send(self.hexStringToBuffer(cmd)) - } else { - self.log('warn', 'Socket not connected') - } - } -} */ - -/* instance.prototype.SetCrosspoint = function (sourceN, destN, levelN) { - var self = this - - self.log('debug', 'Crosspoint ' + sourceN + '>' + destN + ' level ' + levelN) - console.log('SetCrosspoint ' + sourceN + '>' + destN + ' level ' + levelN) - - if (sourceN <= 0 || sourceN > 65536) { - self.log('warn', 'Unable to route source ' + sourceN) - return - } - - if (destN <= 0 || destN > 65536) { - self.log('warn', 'Unable to route destination ' + destN) - return - } - - if (levelN <= 0 || levelN > 256) { - self.log('warn', 'Unable to route level ' + levelN) - return - } - - if (sourceN > 1024 || destN > 1024 || levelN > 16) { - // Extended command required - const COM = '82' - // Matrix - var matrix = self.padLeft((self.config.matrix - 1).toString(16), 2) - // Level - var level = self.padLeft((levelN - 1).toString(16), 2) - // Dest DIV 256 - var destDIV = self.padLeft(Math.floor((destN - 1) / 256).toString(16), 2) - // Destination MOD 256 - var destMOD = self.padLeft(((destN - 1) % 256).toString(16), 2) - // Source DIV 256 - var sourceDIV = self.padLeft(Math.floor((sourceN - 1) / 256).toString(16), 2) - // Source MOD 128 - var sourceMOD = self.padLeft(((sourceN - 1) % 256).toString(16), 2) - // Byte count - var count = '07' - // checksum - var checksum = self.checksum8(COM + matrix + level + destDIV + destMOD + sourceDIV + sourceMOD + count) - // message - var action = COM + matrix + level + destDIV + destMOD + sourceDIV + sourceMOD + count + checksum - } else { - // Standard Command - const COM = '02' - // Matrix and Level - var matrix = (self.config.matrix - 1) << 4 - var level = levelN - 1 - var matrix_level = self.padLeft((matrix | level).toString(16), 2) - // Multiplier if source or dest > 128 - var destDIV = Math.floor((destN - 1) / 128) - var sourceDIV = Math.floor((sourceN - 1) / 128) - var multiplier = self.padLeft(((destDIV << 4) | sourceDIV).toString(16), 2) - // Destination MOD 128 - var dest = self.padLeft(((destN - 1) % 128).toString(16), 2) - // Source MOD 128 - var source = self.padLeft(((sourceN - 1) % 128).toString(16), 2) - // Byte count - var count = '05' - // checksum - var checksum = self.checksum8(COM + matrix_level + multiplier + dest + source + count) - // message - var action = COM + matrix_level + multiplier + dest + source + count + checksum - } - - self.sendMessage(action) -} - -instance.prototype.getCrosspoints = function (destN) { - var self = this - - console.log('GetCrosspoint ' + destN) - - if (destN <= 0 || destN > 65536) { - self.log('warn', 'Unable to get crosspoint destination ' + destN) - return - } - - if (self.config.max_levels > 16 || destN > 1024) { - // Extended commands - const COM = '81' - // Byte count - var count = '05' - // Matrix - var matrix = self.padLeft((self.config.matrix - 1).toString(16), 2) - // Dest DIV 256 - var destDIV = self.padLeft(Math.floor((destN - 1) / 256).toString(16), 2) - // Dest Mod 256 - var destMOD = self.padLeft(((destN - 1) % 256).toString(16), 2) - - // check all levels - for (var i = 0; i <= self.config.max_levels - 1; i++) { - var level = self.padLeft(i.toString(16), 2) - // checksum - var checksum = self.checksum8(COM + matrix + level + destDIV + destMOD + count) - // message - var action = COM + matrix + level + destDIV + destMOD + count + checksum - self.sendMessage(action) - } - } else { - // Standard commands - const COM = '01' - // Byte count - var count = '04' - // Matrix and Level - var matrix = (self.config.matrix - 1) << 4 - // Multiplier if dest > 128 - var destDIV = Math.floor((destN - 1) / 128) - var multiplier = self.padLeft((destDIV << 4).toString(16), 2) - // Destination MOD 128 - var dest = self.padLeft(((destN - 1) % 128).toString(16), 2) - - // check all levels - for (var i = 0; i <= self.config.max_levels - 1; i++) { - var matrix_level = self.padLeft((matrix | i).toString(16), 2) - // checksum - var checksum = self.checksum8(COM + matrix_level + multiplier + dest + count) - // message - var action = COM + matrix_level + multiplier + dest + count + checksum - self.sendMessage(action) - } - } -} */ -/* -instance.prototype.stripNumber = function (str) { - var n = str.indexOf(':') - if (n > 0) { - return str.slice(n + 2) - } else { - return str - } -} -instance.prototype.padLeft = function (nr, n, str) { - return Array(n - String(nr).length + 1).join(str || '0') + nr -} - -instance.prototype.asciiToHex = function (str) { - var arr1 = [] - for (var n = 0, l = str.length; n < l; n++) { - var hex = Number(str.charCodeAt(n)).toString(16) - arr1.push(hex) - } - return arr1.join('') -} - -instance.prototype.hexStringToBuffer = function (str) { - return Buffer.from(str, 'hex') -} - -instance.prototype.getLength = function (str) { - var self = this - - var length = (str.length / 2).toString(16) - return self.padLeft(length, 4) -} - -instance.prototype.checksum8 = function (N) { - // convert input value to upper case - strN = new String(N) - strN = strN.toUpperCase() - - strHex = new String('0123456789ABCDEF') - result = 0 - fctr = 16 - - for (i = 0; i < strN.length; i++) { - if (strN.charAt(i) == ' ') continue - - v = strHex.indexOf(strN.charAt(i)) - if (v < 0) { - result = -1 - break - } - - result += v * fctr - - if (fctr == 16) fctr = 1 - else fctr = 16 - } - - // Calculate 2's complement - result = (~(result & 0xff) + 1) & 0xff - - // Convert result to string - strResult = strHex.charAt(Math.floor(result / 16)) + strHex.charAt(result % 16) - - // console.log('checksum: ' + strResult) - return strResult -} */ -/* -instance_skel.extendedBy(instance) -exports = module.exports = instance - */ \ No newline at end of file +runEntrypoint(SW_P_08, UpgradeScripts) \ No newline at end of file diff --git a/src/keepalive.js b/src/keepalive.js index 9d377c3..d625098 100644 --- a/src/keepalive.js +++ b/src/keepalive.js @@ -1 +1,21 @@ -const keepAliveTimer = 30000 \ No newline at end of file +const keepAliveTimeOut = 30000 + +export function startKeepAliveTimer(){ + if (this.keepAliveTimer) { + clearTimeout(this.keepAliveTimer) + } + this.keepAliveTimer = setTimeout(() => { + this.keepAlive() + }, keepAliveTimeOut) +} + +export function stopKeepAliveTimer(){ + if (this.keepAliveTimer) { + clearTimeout(this.keepAliveTimer) + delete this.keepAliveTimer + } +} + +export function keepAlive(){ + +} diff --git a/src/tcp.js b/src/tcp.js index 3aac816..a404f37 100644 --- a/src/tcp.js +++ b/src/tcp.js @@ -56,6 +56,7 @@ export function sendMessage(message) { if (cmd !== undefined) { if (this.socket !== undefined && this.socket.connected) { this.socket.send(this.hexStringToBuffer(cmd)) + this.startKeepAliveTimer() } else { this.log('warn', 'Socket not connected') } @@ -80,11 +81,12 @@ export async function init_tcp () { this.socket.on('error', function (err) { this.log('error', 'Network error: ' + err.message) this.updateStatus(InstanceStatus.ConnectionFailure, err.message) + this.stopKeepAliveTimer() }) this.socket.on('connect', function () { this.updateStatus(InstanceStatus.Ok, 'Connected') - + this.startKeepAliveTimer() if (this.config.supported_commands_on_connect === true) { // request protocol implementation this.sendMessage('61019E') From 4f29c4fa55b0fbb4aa66672b84944067a36e1ebd Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Fri, 5 Jul 2024 18:45:29 +1000 Subject: [PATCH 03/25] split out decode to seperate function / file --- companion/HELP.md | 3 +- src/config.js | 4 -- src/decode.js | 112 ++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 5 +- src/keepalive.js | 2 +- src/tcp.js | 116 +--------------------------------------------- 6 files changed, 119 insertions(+), 123 deletions(-) create mode 100644 src/decode.js diff --git a/companion/HELP.md b/companion/HELP.md index 9857830..3022e22 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -84,5 +84,4 @@ Some dynamic information is stored in variables which you can access through the ## Version 2.0.0 - Update for Companion 3 -- Add Connection Keep Alive -- Add Tx Message Buffer \ No newline at end of file +- Add Connection Keep Alive \ No newline at end of file diff --git a/src/config.js b/src/config.js index ad9be30..d62d1ba 100644 --- a/src/config.js +++ b/src/config.js @@ -1,15 +1,11 @@ import { Regex } from '@companion-module/base' export async function configUpdated(config) { - this.log('debug','update config') - this.config = config - this.updateVariableDefinitions() this.updateFeedbacks() this.updateActions() - this.init_tcp() this.checkFeedbacks('selected_level', 'selected_level_dest','selected_dest','selected_source') } diff --git a/src/decode.js b/src/decode.js new file mode 100644 index 0000000..1924300 --- /dev/null +++ b/src/decode.js @@ -0,0 +1,112 @@ + +export function decode (data) { + let message = [] + + if (data.length > 0) { + for (let j = 0; j < data.length; j++) { + if (data[j] == 0x10) { + switch (data[j + 1]) { + case 0x02: + console.log('Received SOM') + j++ + continue + //break + + case 0x03: + console.log('Received EOM') + j++ + continue + //break + + case 0x06: + console.log('Received ACK') + j++ + continue + //break + + case 0x10: + // remove repeated byte 0x10 + message.push(data[j]) + j++ + continue + //break + + case 0x15: + console.log('Received NAK') + j++ + continue + //break + + default: + message.push(data[j]) + continue + } + } + message.push(data[j]) + } + } + + if (message.length > 2) { + console.log('message extracted: ' + message) + console.log('Command id: ' + message[0]) + //let requests + //let responses + switch (message[0]) { + // Command + case 0x03: + case 0x04: + // Crosspoint Tally, Crosspoint Connected + this.crosspointConnected(message) + break + + case 0x83: + case 0x84: + // Extended Crosspoint Connected + this.ext_crosspointConnected(message) + break + + case 0x62: + // Protocol Implementation Response + //requests = message[1] + //responses = message[2] + + this.commands = [] + + for (let j = 3; j < message.length - 2; j++) { + this.commands.push(message[j]) + } + + console.log('This router implements: ' + this.commands) + + // request names + if (this.config.read_names_on_connect) { + this.readNames() + } + break + + case 0x6a: + case 0x6b: + // Standard Names Request Reply + this.processLabels(message) + break + + case 0xea: + // Extended Source Names Reply + // Allows for extra Level field in response + this.ext_processSourceLabels(message) + break + + case 0xeb: + // Extended Destination Names Reply + // There is no difference in structure to the standard response + this.processLabels(message) + break + + default: + this.log('warn', 'Unknown response code ' + message[0]) + this.log('debug', message.toString()) + console.log('Unknown response code ' + message[0]) + break + } + } +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index 7f5bc5a..2f5a2b8 100644 --- a/src/index.js +++ b/src/index.js @@ -11,9 +11,10 @@ import UpgradeScripts from './upgrades.js' import UpdateActions from './actions.js' import UpdateFeedbacks from'./feedbacks.js' import UpdatePresets from './presets.js' -import {SetupVariables, UpdateVariableDefinitions} from './variables.js' +import { SetupVariables, UpdateVariableDefinitions } from './variables.js' import * as config from './config.js' import * as crosspoints from './crosspoints.js' +import * as decode from './decode.js' import * as keepalive from './keepalive.js' import * as labels from './labels.js' import * as levels from './levels.js' @@ -26,7 +27,7 @@ import * as util from './util.js' class SW_P_08 extends InstanceBase { constructor(internal) { super(internal) - Object.assign(this, { ...config, ...crosspoints, ...keepalive, ...labels, ...levels, ...names, ...tcp, ...util }) + Object.assign(this, { ...config, ...crosspoints, ...decode, ...keepalive, ...labels, ...levels, ...names, ...tcp, ...util }) } async init(config){ diff --git a/src/keepalive.js b/src/keepalive.js index d625098..4d5d43e 100644 --- a/src/keepalive.js +++ b/src/keepalive.js @@ -17,5 +17,5 @@ export function stopKeepAliveTimer(){ } export function keepAlive(){ - + //this.sendMessage('61019E') what message to send here? } diff --git a/src/tcp.js b/src/tcp.js index a404f37..a171ce6 100644 --- a/src/tcp.js +++ b/src/tcp.js @@ -99,123 +99,11 @@ export async function init_tcp () { // send ACK this.sendAck() // Decode - this.socket.emit('decode', chunk) + this.decode(chunk) receivebuffer = chunk } else { // duplicate - console.log('Repeated: ' + chunk.length + ' bytes') - } - }) - - this.socket.on('decode', function (data) { - let message = [] - - if (data.length > 0) { - for (let j = 0; j < data.length; j++) { - if (data[j] == 0x10) { - switch (data[j + 1]) { - case 0x02: - console.log('Received SOM') - j++ - continue - //break - - case 0x03: - console.log('Received EOM') - j++ - continue - //break - - case 0x06: - console.log('Received ACK') - j++ - continue - //break - - case 0x10: - // remove repeated byte 0x10 - message.push(data[j]) - j++ - continue - //break - - case 0x15: - console.log('Received NAK') - j++ - continue - //break - - default: - message.push(data[j]) - continue - } - } - message.push(data[j]) - } - } - - if (message.length > 2) { - console.log('message extracted: ' + message) - console.log('Command id: ' + message[0]) - //let requests - //let responses - switch (message[0]) { - // Command - case 0x03: - case 0x04: - // Crosspoint Tally, Crosspoint Connected - this.crosspointConnected(message) - break - - case 0x83: - case 0x84: - // Extended Crosspoint Connected - this.ext_crosspointConnected(message) - break - - case 0x62: - // Protocol Implementation Response - //requests = message[1] - //responses = message[2] - - this.commands = [] - - for (let j = 3; j < message.length - 2; j++) { - this.commands.push(message[j]) - } - - console.log('This router implements: ' + this.commands) - - // request names - if (this.config.read_names_on_connect) { - this.readNames() - } - break - - case 0x6a: - case 0x6b: - // Standard Names Request Reply - this.processLabels(message) - break - - case 0xea: - // Extended Source Names Reply - // Allows for extra Level field in response - this.ext_processSourceLabels(message) - break - - case 0xeb: - // Extended Destination Names Reply - // There is no difference in structure to the standard response - this.processLabels(message) - break - - default: - this.log('warn', 'Unknown response code ' + message[0]) - this.log('debug', message.toString()) - console.log('Unknown response code ' + message[0]) - break - } + console.log(`Repeated: ${chunk.length} bytes`) } }) } From 132c5f1d1b511b4d45297872da393f4190426613 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Fri, 5 Jul 2024 18:49:58 +1000 Subject: [PATCH 04/25] rework configUpdated --- src/config.js | 4 +++- src/index.js | 6 +----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/config.js b/src/config.js index d62d1ba..e311cee 100644 --- a/src/config.js +++ b/src/config.js @@ -1,11 +1,13 @@ -import { Regex } from '@companion-module/base' +import { InstanceStatus, Regex } from '@companion-module/base' export async function configUpdated(config) { this.log('debug','update config') + this.updateStatus(InstanceStatus.Connecting) this.config = config this.updateVariableDefinitions() this.updateFeedbacks() this.updateActions() + this.initPresets() this.init_tcp() this.checkFeedbacks('selected_level', 'selected_level_dest','selected_dest','selected_source') } diff --git a/src/index.js b/src/index.js index 2f5a2b8..ca10d86 100644 --- a/src/index.js +++ b/src/index.js @@ -31,18 +31,14 @@ class SW_P_08 extends InstanceBase { } async init(config){ - this.updateStatus(InstanceStatus.Connecting) this.config = config - this.updateVariableDefinitions() this.updateFeedbacks() this.updateActions() this.initPresets() - - this.checkFeedbacks('selected_level', 'selected_level_dest','selected_dest','selected_source') - this.init_tcp() + this.checkFeedbacks('selected_level', 'selected_level_dest', 'selected_dest', 'selected_source') } // When module gets deleted From 0d9b1dc6f96fe8673df136f38787f02e659706c1 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Fri, 5 Jul 2024 20:59:03 +1000 Subject: [PATCH 05/25] refactor actionOptions, feedbackOptions fix config regex minor fixes --- src/actions.js | 462 ++++++++++++++++++----------------------------- src/config.js | 4 +- src/consts.js | 82 +++++++++ src/feedbacks.js | 48 +---- src/index.js | 8 +- src/variables.js | 2 +- 6 files changed, 271 insertions(+), 335 deletions(-) diff --git a/src/actions.js b/src/actions.js index 0b9bf59..577b2d9 100644 --- a/src/actions.js +++ b/src/actions.js @@ -1,305 +1,191 @@ +import { actionOptions } from './consts.js' + export async function UpdateActions(self) { - let actionDefinitions = [] + let actionDefinitions = [] actionDefinitions['select_level'] = { name: 'Select Levels', options: [ { - type: 'multidropdown', - label: 'Levels', - id: 'level', - default: [1], - choices: this.levels, - minSelection: 1, + ...actionOptions.levels, + choices: self.levels, }, ], callback: async ({ options }) => { - self.processLevelsSelection(options.level, true) + self.processLevelsSelection(options.level, true) }, } - actionDefinitions['deselect_level'] = { - name: 'De-Select Levels', - options: [ - { - type: 'multidropdown', - label: 'Levels', - id: 'level', - default: [1], - choices: this.levels, - minSelection: 1, - }, - ], - callback: async ({ options }) => { - self.processLevelsSelection(options.level, false) - }, - } - actionDefinitions['toggle_level'] = { - name: 'Toggle Levels', - options: [ - { - type: 'multidropdown', - label: 'Levels', - id: 'level', - default: [1], - choices: this.levels, - minSelection: 1, - }, - ], - callback: async ({ options }) => { - self.processLevelsSelection(options.level, 'toggle') - }, - } - actionDefinitions['select_dest'] = { - name: 'Select Destination', - options: [ - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, - max: 65536, - }, - ], - callback: async ({ options }) => { - self.selected_dest = parseInt(options.dest) - self.getCrosspoints(options.dest) - console.log('set destination ' + self.selected_dest) - self.setVariableValues({ Destination : self.selected_dest}) - self.checkFeedbacks('selected_dest', 'selected_level_dest') - }, - } - actionDefinitions['select_dest_name'] = { - name: 'Select Destination name', - options: [ - { - type: 'dropdown', - label: 'Destination', - id: 'dest', - default: 1, - choices: this.dest_names, - }, - ], - callback: async ({ options }) => { - self.selected_dest = parseInt(options.dest) - self.getCrosspoints(options.dest) - console.log('set destination ' + self.selected_dest) - self.setVariableValues({ Destination: self.selected_dest }) - self.checkFeedbacks('selected_dest', 'selected_level_dest') - }, - } - actionDefinitions['select_source'] = { - name: 'Select Source', - options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - max: 65536, - }, - ], - callback: async ({ options }) => { - self.selected_source = parseInt(options.source) - console.log('set source ' + self.selected_source) - self.setVariableValues({ Source : self.selected_source }) - self.checkFeedbacks('selected_source') - }, - } - actionDefinitions['select_source_name'] = { - name: 'Select Source name', - options: [ - { - type: 'dropdown', - label: 'Source', - id: 'source', - default: 1, - choices: this.source_names, - }, - ], - callback: async ({ options }) => { - self.selected_source = parseInt(options.source) - console.log('set source ' + self.selected_source) - self.setVariableValues({ Source: self.selected_source }) - self.checkFeedbacks('selected_source') - }, - } - actionDefinitions['route_source'] = { - name: 'Route Source to selected Levels and Destination', - options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - max: 65536, - }, - ], - callback: async ({ options }) => { - console.log(self.selected_level) - const l = self.selected_level.length - for (let i = 0; i < l; i++) { - if (self.selected_level[i].enabled === true) { - self.SetCrosspoint(options.source, self.selected_dest, self.selected_level[i].id) - } + actionDefinitions['deselect_level'] = { + name: 'De-Select Levels', + options: [ + { + ...actionOptions.levels, + choices: self.levels, + }, + ], + callback: async ({ options }) => { + self.processLevelsSelection(options.level, false) + }, + } + actionDefinitions['toggle_level'] = { + name: 'Toggle Levels', + options: [ + { + ...actionOptions.levels, + choices: self.levels, + }, + ], + callback: async ({ options }) => { + self.processLevelsSelection(options.level, 'toggle') + }, + } + actionDefinitions['select_dest'] = { + name: 'Select Destination', + options: [ actionOptions.destination ], + callback: async ({ options }) => { + self.selected_dest = parseInt(options.dest) + self.getCrosspoints(options.dest) + console.log('set destination ' + self.selected_dest) + self.setVariableValues({ Destination: self.selected_dest }) + self.checkFeedbacks('selected_dest', 'selected_level_dest') + }, + } + actionDefinitions['select_dest_name'] = { + name: 'Select Destination name', + options: [{ ...actionOptions.destinationName, choices: self.dest_names }], + callback: async ({ options }) => { + self.selected_dest = parseInt(options.dest) + self.getCrosspoints(options.dest) + console.log('set destination ' + self.selected_dest) + self.setVariableValues({ Destination: self.selected_dest }) + self.checkFeedbacks('selected_dest', 'selected_level_dest') + }, + } + actionDefinitions['select_source'] = { + name: 'Select Source', + options: [ actionOptions.source ], + callback: async ({ options }) => { + self.selected_source = parseInt(options.source) + console.log('set source ' + self.selected_source) + self.setVariableValues({ Source: self.selected_source }) + self.checkFeedbacks('selected_source') + }, + } + actionDefinitions['select_source_name'] = { + name: 'Select Source name', + options: [{ ...actionOptions.sourceName, choices: self.source_names }], + callback: async ({ options }) => { + self.selected_source = parseInt(options.source) + console.log('set source ' + self.selected_source) + self.setVariableValues({ Source: self.selected_source }) + self.checkFeedbacks('selected_source') + }, + } + actionDefinitions['route_source'] = { + name: 'Route Source to selected Levels and Destination', + options: [ actionOptions.source ], + callback: async ({ options }) => { + console.log(self.selected_level) + const l = self.selected_level.length + for (let i = 0; i < l; i++) { + if (self.selected_level[i].enabled === true) { + self.SetCrosspoint(options.source, self.selected_dest, self.selected_level[i].id) } - }, - } - actionDefinitions['route_source_name'] = { - name: 'Route Source name to selected Levels and Destination', - options: [ - { - type: 'dropdown', - label: 'Source', - id: 'source', - default: 1, - choices: this.source_names, - }, - ], - callback: async ({ options }) => { - console.log(self.selected_level) - const l = self.selected_level.length - for (let i = 0; i < l; i++) { - if (self.selected_level[i].enabled === true) { - self.SetCrosspoint(options.source, self.selected_dest, self.selected_level[i].id) - } + } + }, + } + actionDefinitions['route_source_name'] = { + name: 'Route Source name to selected Levels and Destination', + options: [{ ...actionOptions.sourceName, choices: self.source_names }], + callback: async ({ options }) => { + console.log(self.selected_level) + const l = self.selected_level.length + for (let i = 0; i < l; i++) { + if (self.selected_level[i].enabled === true) { + self.SetCrosspoint(options.source, self.selected_dest, self.selected_level[i].id) } - }, - } - actionDefinitions['take'] = { - name: 'Take', - options: [], - callback: async () => { - console.log(self.selected_level) - const l = self.selected_level.length - for (let i = 0; i < l; i++) { - if (self.selected_level[i].enabled === true) { - self.SetCrosspoint(self.selected_source, self.selected_dest, self.selected_level[i].id) - } + } + }, + } + actionDefinitions['take'] = { + name: 'Take', + options: [], + callback: async () => { + console.log(self.selected_level) + const l = self.selected_level.length + for (let i = 0; i < l; i++) { + if (self.selected_level[i].enabled === true) { + self.SetCrosspoint(self.selected_source, self.selected_dest, self.selected_level[i].id) + } + } + }, + } + actionDefinitions['clear'] = { + name: 'Clear', + options: [ actionOptions.clear, actionOptions.clearEnableLevels ], + callback: async ({ options }) => { + if (options.clear === 'all' || options.clear === 'level') { + self.selected_level = [] + for (let i = 1; i <= self.config.max_levels; i++) { + self.selected_level.push({ id: i, enabled: options.clear_enable_levels }) } - }, - } - actionDefinitions['clear'] = { - name: 'Clear', - options: [ - { - type: 'dropdown', - label: 'Clear', - id: 'clear', - default: 'all', - choices: [ - { id: 'all', label: 'All' }, - { id: 'level', label: 'Levels' }, - { id: 'dest', label: 'Destination' }, - { id: 'source', label: 'Source' }, - ], - }, - { - type: 'checkbox', - label: "Enable all levels on 'Clear All' or 'Clear Levels'", - id: 'clear_enable_levels', - default: true, - }, - ], - callback: async ({ options }) => { - if (options.clear === 'all' || options.clear === 'level') { - self.selected_level = [] - for (let i = 1; i <= self.config.max_levels; i++) { - self.selected_level.push({ id: i, enabled: options.clear_enable_levels }) - } - self.checkFeedbacks('selected_level', 'selected_level_dest') - console.log('clear levels') - console.log(self.selected_level) - } + self.checkFeedbacks('selected_level', 'selected_level_dest') + console.log('clear levels') + console.log(self.selected_level) + } - if (options.clear === 'all' || options.clear === 'dest') { - self.selected_dest = 0 - self.setVariableValues({ Destination : self.selected_dest }) - self.checkFeedbacks('selected_dest', 'selected_level_dest') - console.log('clear dest') - } + if (options.clear === 'all' || options.clear === 'dest') { + self.selected_dest = 0 + self.setVariableValues({ Destination: self.selected_dest }) + self.checkFeedbacks('selected_dest', 'selected_level_dest') + console.log('clear dest') + } - if (options.clear === 'all' || options.clear === 'source') { - self.selected_source = 0 - self.setVariableValues({ Source : self.selected_source }) - self.checkFeedbacks('selected_source', 'clear source') - } - }, - } - actionDefinitions['set_crosspoint'] = { - name: 'Set crosspoint', - options: [ - { - type: 'multidropdown', - label: 'Levels', - id: 'level', - default: [1], - choices: this.levels, - minSelection: 1, - }, - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - max: 65536, - }, - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, - max: 65536, - }, - ], - callback: async ({ options }) => { - for (let level_val of options.level) { - self.SetCrosspoint(options.source, options.dest, level_val) - } - }, - } - actionDefinitions['set_crosspoint_name'] = { - name: 'Set crosspoint by name', - options: [ - { - type: 'multidropdown', - label: 'Levels', - id: 'level', - default: [1], - choices: this.levels, - minSelection: 1, - }, - { - type: 'dropdown', - label: 'Source', - id: 'source', - default: 1, - choices: this.source_names, - }, - { - type: 'dropdown', - label: 'Destination', - id: 'dest', - default: 1, - choices: this.dest_names, - }, - ], - callback: async ({ options }) => { - for (let level_val of options.level) { - self.SetCrosspoint(options.source, options.dest, level_val) - } - }, - } - actionDefinitions['get_names'] = { - name: 'Refresh Source and Destination names', - options: [], - callback: async () => { - self.readNames() - }, - } - self.setActionDefinitions(actionDefinitions) + if (options.clear === 'all' || options.clear === 'source') { + self.selected_source = 0 + self.setVariableValues({ Source: self.selected_source }) + self.checkFeedbacks('selected_source', 'clear source') + } + }, + } + actionDefinitions['set_crosspoint'] = { + name: 'Set crosspoint', + options: [ + { + ...actionOptions.levels, + choices: self.levels, + }, + actionOptions.source, + actionOptions.destination, + ], + callback: async ({ options }) => { + for (let level_val of options.level) { + self.SetCrosspoint(options.source, options.dest, level_val) + } + }, + } + actionDefinitions['set_crosspoint_name'] = { + name: 'Set crosspoint by name', + options: [ + { + ...actionOptions.levels, + choices: this.levels, + }, + { ...actionOptions.sourceName, choices: self.source_names }, + { ...actionOptions.destinationName, choices: self.dest_names }, + ], + callback: async ({ options }) => { + for (let level_val of options.level) { + self.SetCrosspoint(options.source, options.dest, level_val) + } + }, + } + actionDefinitions['get_names'] = { + name: 'Refresh Source and Destination names', + options: [], + callback: async () => { + self.readNames() + }, + } + self.setActionDefinitions(actionDefinitions) } diff --git a/src/config.js b/src/config.js index e311cee..4ecc7ef 100644 --- a/src/config.js +++ b/src/config.js @@ -26,7 +26,7 @@ export function getConfigFields() { id: 'host', label: 'Device IP', width: 6, - regex: Regex.REGEX_IP, + regex: Regex.IP, }, { type: 'textinput', @@ -34,7 +34,7 @@ export function getConfigFields() { label: 'Device Port', width: 6, default: '8910', - regex: Regex.REGEX_PORT, + regex: Regex.PORT, }, { type: 'number', diff --git a/src/consts.js b/src/consts.js index d24be32..faf2153 100644 --- a/src/consts.js +++ b/src/consts.js @@ -15,3 +15,85 @@ export const colours = { cyan: combineRgb(102, 255, 255), orange: combineRgb(255, 191, 128) } + +export const feedbackOptions = { + levels: { + type: 'multidropdown', + label: 'Levels', + id: 'level', + default: [1], + minSelection: 1, + }, + destination: { + type: 'number', + label: 'Destination', + id: 'dest', + default: 1, + min: 1, + max: 65536, + }, + source: { + type: 'number', + label: 'Source', + id: 'source', + default: 1, + min: 1, + max: 65536, + }, +} + +export const actionOptions = { + levels: { + type: 'multidropdown', + label: 'Levels', + id: 'level', + default: [1], + minSelection: 1, + }, + destination: { + type: 'number', + label: 'Destination', + id: 'dest', + default: 1, + min: 1, + max: 65536, + }, + destinationName: { + type: 'dropdown', + label: 'Destination', + id: 'dest', + default: 1, + }, + source: { + type: 'number', + label: 'Source', + id: 'source', + default: 1, + min: 1, + max: 65536, + }, + sourceName: { + type: 'dropdown', + label: 'Source', + id: 'source', + default: 1, + }, + clear: { + type: 'dropdown', + label: 'Clear', + id: 'clear', + default: 'all', + choices: [ + { id: 'all', label: 'All' }, + { id: 'level', label: 'Levels' }, + { id: 'dest', label: 'Destination' }, + { id: 'source', label: 'Source' }, + ], + }, + clearEnableLevels: { + type: 'checkbox', + label: "Enable all levels on 'Clear All' or 'Clear Levels'", + id: 'clear_enable_levels', + default: true, + }, +} diff --git a/src/feedbacks.js b/src/feedbacks.js index 669500c..9ad0ac3 100644 --- a/src/feedbacks.js +++ b/src/feedbacks.js @@ -1,4 +1,4 @@ -import { colours } from './consts.js' +import { colours, feedbackOptions } from './consts.js' export async function UpdateFeedbacks(self) { @@ -15,12 +15,8 @@ export async function UpdateFeedbacks(self) { }, options: [ { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], + ...feedbackOptions.levels, choices: self.levels, - minSelection: 1, }, ], callback: async (feedback) => { @@ -49,24 +45,14 @@ export async function UpdateFeedbacks(self) { description: 'Change colour of button on selected levels and destination', style: { color: colours.black, - bgcolor:colours.purple, + bgcolor: colours.purple, }, options: [ { - type: 'multiselect', - label: 'Levels', - id: 'level', - default: [1], + ...feedbackOptions.levels, choices: self.levels, - minSelection: 1, - }, - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, }, + feedbackOptions.destination, ], callback: async (feedback) => { if (self.selected_dest === feedback.options.dest) { @@ -101,13 +87,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.green, }, options: [ - { - type: 'number', - label: 'Destination', - id: 'dest', - default: 1, - min: 1, - }, + feedbackOptions.destination, ], callback: async (feedback) => { if (self.selected_dest === feedback.options.dest) { @@ -127,13 +107,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.cyan, }, options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - }, + feedbackOptions.source, ], callback: async (feedback) => { if (self.selected_source === feedback.options.source) { @@ -153,13 +127,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.orange, }, options: [ - { - type: 'number', - label: 'Source', - id: 'source', - default: 1, - min: 1, - }, + feedbackOptions.source, ], callback: async (feedback) => { // look for this dest in route table diff --git a/src/index.js b/src/index.js index ca10d86..1fa2821 100644 --- a/src/index.js +++ b/src/index.js @@ -7,10 +7,10 @@ // Updated for Companion v3 July 2024, Phillip Ivan Pietruschka import { InstanceBase, runEntrypoint, InstanceStatus } from '@companion-module/base' -import UpgradeScripts from './upgrades.js' -import UpdateActions from './actions.js' -import UpdateFeedbacks from'./feedbacks.js' -import UpdatePresets from './presets.js' +import { UpgradeScripts } from './upgrades.js' +import { UpdateActions } from './actions.js' +import { UpdateFeedbacks } from'./feedbacks.js' +import { UpdatePresets } from './presets.js' import { SetupVariables, UpdateVariableDefinitions } from './variables.js' import * as config from './config.js' import * as crosspoints from './crosspoints.js' diff --git a/src/variables.js b/src/variables.js index b14f1fb..49e6075 100644 --- a/src/variables.js +++ b/src/variables.js @@ -34,7 +34,7 @@ export async function SetupVariables(self) { self.setVariable('Destination', self.selected_dest) } -export async function UpdateVariableDefinition(self) { +export async function UpdateVariableDefinitions(self) { let coreVariables = [] coreVariables.push( From 98232b04048b29585ff4ef816b5baf17c60284a9 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Fri, 5 Jul 2024 21:09:13 +1000 Subject: [PATCH 06/25] formatting --- src/actions.js | 31 +++++-------------------------- src/feedbacks.js | 25 +++++-------------------- 2 files changed, 10 insertions(+), 46 deletions(-) diff --git a/src/actions.js b/src/actions.js index 577b2d9..8e2c4e7 100644 --- a/src/actions.js +++ b/src/actions.js @@ -5,36 +5,21 @@ export async function UpdateActions(self) { actionDefinitions['select_level'] = { name: 'Select Levels', - options: [ - { - ...actionOptions.levels, - choices: self.levels, - }, - ], + options: [{ ...actionOptions.levels, choices: self.levels }], callback: async ({ options }) => { self.processLevelsSelection(options.level, true) }, } actionDefinitions['deselect_level'] = { name: 'De-Select Levels', - options: [ - { - ...actionOptions.levels, - choices: self.levels, - }, - ], + options: [{ ...actionOptions.levels, choices: self.levels }], callback: async ({ options }) => { self.processLevelsSelection(options.level, false) }, } actionDefinitions['toggle_level'] = { name: 'Toggle Levels', - options: [ - { - ...actionOptions.levels, - choices: self.levels, - }, - ], + options: [{ ...actionOptions.levels, choices: self.levels }], callback: async ({ options }) => { self.processLevelsSelection(options.level, 'toggle') }, @@ -151,10 +136,7 @@ export async function UpdateActions(self) { actionDefinitions['set_crosspoint'] = { name: 'Set crosspoint', options: [ - { - ...actionOptions.levels, - choices: self.levels, - }, + { ...actionOptions.levels, choices: self.levels }, actionOptions.source, actionOptions.destination, ], @@ -167,10 +149,7 @@ export async function UpdateActions(self) { actionDefinitions['set_crosspoint_name'] = { name: 'Set crosspoint by name', options: [ - { - ...actionOptions.levels, - choices: this.levels, - }, + { ...actionOptions.levels, choices: this.levels }, { ...actionOptions.sourceName, choices: self.source_names }, { ...actionOptions.destinationName, choices: self.dest_names }, ], diff --git a/src/feedbacks.js b/src/feedbacks.js index 9ad0ac3..1797a43 100644 --- a/src/feedbacks.js +++ b/src/feedbacks.js @@ -1,6 +1,5 @@ import { colours, feedbackOptions } from './consts.js' - export async function UpdateFeedbacks(self) { // feedback let feedbacks = {} @@ -13,12 +12,7 @@ export async function UpdateFeedbacks(self) { color: colours.black, bgcolor: colours.purple, }, - options: [ - { - ...feedbackOptions.levels, - choices: self.levels, - }, - ], + options: [{ ...feedbackOptions.levels, choices: self.levels }], callback: async (feedback) => { let l = feedback.options.level.length let k = self.selected_level.length @@ -48,10 +42,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.purple, }, options: [ - { - ...feedbackOptions.levels, - choices: self.levels, - }, + { ...feedbackOptions.levels, choices: self.levels }, feedbackOptions.destination, ], callback: async (feedback) => { @@ -86,9 +77,7 @@ export async function UpdateFeedbacks(self) { color: colours.black, bgcolor: colours.green, }, - options: [ - feedbackOptions.destination, - ], + options: [ feedbackOptions.destination ], callback: async (feedback) => { if (self.selected_dest === feedback.options.dest) { return true @@ -106,9 +95,7 @@ export async function UpdateFeedbacks(self) { color: colours.black, bgcolor: colours.cyan, }, - options: [ - feedbackOptions.source, - ], + options: [ feedbackOptions.source ], callback: async (feedback) => { if (self.selected_source === feedback.options.source) { return true @@ -126,9 +113,7 @@ export async function UpdateFeedbacks(self) { color: colours.black, bgcolor: colours.orange, }, - options: [ - feedbackOptions.source, - ], + options: [ feedbackOptions.source ], callback: async (feedback) => { // look for this dest in route table console.log('dest:source feedback ' + self.selected_dest + ':' + feedback.options.source) From 865408af988d6626f82c542b020ba5cb3abdab0b Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Fri, 5 Jul 2024 21:16:42 +1000 Subject: [PATCH 07/25] update companion-module-tools to 1.5.1 --- package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 45c63d0..6c67772 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@companion-module/base": "~1.8.0" }, "devDependencies": { - "@companion-module/tools": "^1.5.0" + "@companion-module/tools": "^1.5.1" }, "prettier": "@companion-module/tools/.prettierrc.json" } diff --git a/yarn.lock b/yarn.lock index 9106d0b..5be7815 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,7 +19,7 @@ p-timeout "^4.1.0" tslib "^2.6.2" -"@companion-module/tools@^1.5.0": +"@companion-module/tools@^1.5.1": version "1.5.1" resolved "https://registry.yarnpkg.com/@companion-module/tools/-/tools-1.5.1.tgz#5f5d3e65eee22926e704de3084e3f66de9d841d0" integrity sha512-9KJC0mZLpg7dlS3MKCYzbUbOjiDMpjjwHgeAxMzh9AOM1ybgsEkmYLfIyZ/EWWSSJ/1s75IJmOtIUWVkZUuqhQ== From c757e9b3bac0ff6586189fbcd2deb17718f306e0 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 15:03:24 +1000 Subject: [PATCH 08/25] declare unchanging variables as const startKeepAlive on sendAck. --- src/crosspoints.js | 26 +++++++++++++------------- src/labels.js | 20 ++++++++++---------- src/tcp.js | 1 + src/util.js | 4 ++-- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/crosspoints.js b/src/crosspoints.js index 4a3228c..59f3dcd 100644 --- a/src/crosspoints.js +++ b/src/crosspoints.js @@ -1,11 +1,11 @@ export function crosspointConnected (data) { - //let matrix = ((data[1] & 0xf0) >> 4) + 1 unused - let level = (data[1] & 0x0f) + 1 - let destDiv = (data[2] & 0x70) >> 4 + //const matrix = ((data[1] & 0xf0) >> 4) + 1 unused + const level = (data[1] & 0x0f) + 1 + const destDiv = (data[2] & 0x70) >> 4 let sourceDiv = data[2] & 0x7 - let dest = 128 * destDiv + data[3] + 1 - let source = 128 * sourceDiv + data[4] + 1 + const dest = 128 * destDiv + data[3] + 1 + const source = 128 * sourceDiv + data[4] + 1 console.log('Source ' + source + ' routed to ' + dest + ' on level ' + level) this.log('debug', 'Source ' + source + ' routed to destination ' + dest + ' on level ' + level) @@ -14,14 +14,14 @@ export function crosspointConnected (data) { } export function ext_crosspointConnected (data) { - //let matrix = data[1] + 1 - let level = data[2] + 1 - let destDiv = data[3] * 256 - let destMod = data[4] - let sourceDiv = data[5] * 256 - let sourceMod = data[6] - let dest = destDiv + destMod + 1 - let source = sourceDiv + sourceMod + 1 + //const matrix = data[1] + 1 + const level = data[2] + 1 + const destDiv = data[3] * 256 + const destMod = data[4] + const sourceDiv = data[5] * 256 + const sourceMod = data[6] + const dest = destDiv + destMod + 1 + const source = sourceDiv + sourceMod + 1 console.log('Source ' + source + ' routed to ' + dest + ' on level ' + level) this.log('debug', 'Source ' + source + ' routed to destination ' + dest + ' on level ' + level) diff --git a/src/labels.js b/src/labels.js index 1c0628e..704bd54 100644 --- a/src/labels.js +++ b/src/labels.js @@ -1,26 +1,26 @@ export function processLabels (data) { - let char_length_table = [4, 8, 12] + const char_length_table = [4, 8, 12] // byte1 = matrix (& level for sources) - let char_length = char_length_table[data[2]] - let label_number = 256 * data[3] + data[4] - let labels_in_part = data[5] - let start = 6 + const char_length = char_length_table[data[2]] + const label_number = 256 * data[3] + data[4] + const labels_in_part = data[5] + const start = 6 this.extractLabels(data, char_length, label_number, labels_in_part, start) } export function ext_processSourceLabels (data) { - let char_length_table = [4, 8, 12] + const char_length_table = [4, 8, 12] // byte1 = matrix number // byte2 = level number - let char_length = char_length_table[data[3]] - let label_number = 256 * data[4] + data[5] - let labels_in_part = data[6] - let start = 7 + const char_length = char_length_table[data[3]] + const label_number = 256 * data[4] + data[5] + const labels_in_part = data[6] + const start = 7 this.extractLabels(data, char_length, label_number, labels_in_part, start) } diff --git a/src/tcp.js b/src/tcp.js index a171ce6..7f91fe2 100644 --- a/src/tcp.js +++ b/src/tcp.js @@ -7,6 +7,7 @@ export function sendAck() { this.log('debug','Sending ACK') if (this.socket !== undefined && this.socket.connected) { this.socket.send(this.hexStringToBuffer('1006')) + this.startKeepAliveTimer() } else { this.log('warn','Socket not connected :(') } diff --git a/src/util.js b/src/util.js index e983109..8e5c37c 100644 --- a/src/util.js +++ b/src/util.js @@ -1,7 +1,7 @@ import { Buffer } from 'node:buffer' export function stripNumber(str) { - let n = str.indexOf(':') + const n = str.indexOf(':') if (n > 0) { return str.slice(n + 2) } else { @@ -27,7 +27,7 @@ export function hexStringToBuffer(str) { } export function getLength(str) { - let length = (str.length / 2).toString(16) + const length = (str.length / 2).toString(16) return this.padLeft(length, 4) } From 7c93700b65efc38027dbbcea54f66a9d3e813ba1 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 15:18:53 +1000 Subject: [PATCH 09/25] accept variables for sourceName and destinationName action option fields, and corresponding actions. --- src/actions.js | 31 ++++++++++++++++++++++++++----- src/consts.js | 4 ++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/actions.js b/src/actions.js index 8e2c4e7..9157951 100644 --- a/src/actions.js +++ b/src/actions.js @@ -39,8 +39,13 @@ export async function UpdateActions(self) { name: 'Select Destination name', options: [{ ...actionOptions.destinationName, choices: self.dest_names }], callback: async ({ options }) => { - self.selected_dest = parseInt(options.dest) - self.getCrosspoints(options.dest) + const dest = parseInt(await self.parseVaraiblesInString(options.dest)) + if (isNaN(dest) || dest < 1 || dest > 65536) { + self.log('warn', `select_dest_name has been passed an out of range variable ${dest}`) + return undefined + } + self.selected_dest = dest + self.getCrosspoints(dest) console.log('set destination ' + self.selected_dest) self.setVariableValues({ Destination: self.selected_dest }) self.checkFeedbacks('selected_dest', 'selected_level_dest') @@ -60,7 +65,12 @@ export async function UpdateActions(self) { name: 'Select Source name', options: [{ ...actionOptions.sourceName, choices: self.source_names }], callback: async ({ options }) => { - self.selected_source = parseInt(options.source) + const source = parseInt(await self.parseVaraiblesInString(options.source)) + if (isNaN(source) || source < 1 || source > 65536) { + self.log('warn', `select_source_name has been passed an out of range variable ${source}`) + return undefined + } + self.selected_source = source console.log('set source ' + self.selected_source) self.setVariableValues({ Source: self.selected_source }) self.checkFeedbacks('selected_source') @@ -83,11 +93,16 @@ export async function UpdateActions(self) { name: 'Route Source name to selected Levels and Destination', options: [{ ...actionOptions.sourceName, choices: self.source_names }], callback: async ({ options }) => { + const source = parseInt(await self.parseVaraiblesInString(options.source)) + if (isNaN(source) || source < 1 || source > 65536) { + self.log('warn', `route_source_name has been passed an out of range variable ${source}`) + return undefined + } console.log(self.selected_level) const l = self.selected_level.length for (let i = 0; i < l; i++) { if (self.selected_level[i].enabled === true) { - self.SetCrosspoint(options.source, self.selected_dest, self.selected_level[i].id) + self.SetCrosspoint(source, self.selected_dest, self.selected_level[i].id) } } }, @@ -154,8 +169,14 @@ export async function UpdateActions(self) { { ...actionOptions.destinationName, choices: self.dest_names }, ], callback: async ({ options }) => { + const source = parseInt(await self.parseVaraiblesInString(options.source)) + const dest = parseInt(await self.parseVaraiblesInString(options.dest)) + if (isNaN(source) || source < 1 || source > 65536 || isNaN(dest) || dest < 1 || dest > 65536) { + self.log('warn', `set_crosspoint_name has been passed an out of range variable - src ${source} : dst ${dest}`) + return undefined + } for (let level_val of options.level) { - self.SetCrosspoint(options.source, options.dest, level_val) + self.SetCrosspoint(source, dest, level_val) } }, } diff --git a/src/consts.js b/src/consts.js index faf2153..0fe74c9 100644 --- a/src/consts.js +++ b/src/consts.js @@ -63,6 +63,8 @@ export const actionOptions = { label: 'Destination', id: 'dest', default: 1, + allowCustom: true, + tooltip: 'Accepts Variable. Should return an integer between 1 & 65536', }, source: { type: 'number', @@ -77,6 +79,8 @@ export const actionOptions = { label: 'Source', id: 'source', default: 1, + allowCustom: true, + tooltip: 'Accepts Variable. Should return an integer between 1 & 65536', }, clear: { type: 'dropdown', From c3597654731a953849c4fb5f5566a5516ebd4486 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 18:04:24 +1000 Subject: [PATCH 10/25] define commands as constants keepalive sends dummy packet fixup presets for V3 --- src/consts.js | 53 +++++++++++++++++++++++++++++++++++++- src/crosspoints.js | 25 +++++++++--------- src/decode.js | 32 ++++++++++++----------- src/keepalive.js | 5 ++-- src/names.js | 16 ++++++------ src/presets.js | 63 +++++++++++++++++++++------------------------- src/tcp.js | 9 +++---- 7 files changed, 127 insertions(+), 76 deletions(-) diff --git a/src/consts.js b/src/consts.js index 0fe74c9..b6408c1 100644 --- a/src/consts.js +++ b/src/consts.js @@ -1,11 +1,12 @@ import { combineRgb } from '@companion-module/base' export const msgDelay = 5 +export const keepAliveTimeOut = 30000 export const DLE = '10' export const STX = '02' export const ETX = '03' - +export const ACK = '06' export const colours = { white: combineRgb(255, 255, 255), black: combineRgb(0, 0, 0), @@ -101,3 +102,53 @@ export const actionOptions = { default: true, }, } +export const presetDefaults = { + style: { + size: '18', + show_topbar: 'default', + alignment: 'center:center', + color: colours.white, + bgcolor: colours.black, + }, +} + +export const cmd = { + interrogate: '01', + connect: '02', + tally: '03', + connected: '04', + tallyDumpRequest: '21', + tallyDumpByteMessage: '22', + tallyDumpWordMessage: '23', + getSourceName: '64', + getDestName: '66', + connectOnGoGroupSalvo: '78', + goGroupSalvo: '79', + connectOnGoGroupSalvoAck: '7A', + goDoneGroupSalvoAck: '7B', + salvoGroupInterrogate: '7C', + groupSalvoTally: '7D', + extendedinterrogate: '81', + extendedConnect: '82', + extendedGetSourceName: 'E4', + extendedGetDestName: 'E6', +} + +export const hexBytes = { + DLE: 0x10, + STX: 0x02, + ETX: 0x03, + ACK: 0x06, + NAK: 0x15, + cmd: { + tally: 0x03, + connected: 0x04, + extendedTally: 0x83, + extendedConnected: 0x84, + protocolImplementation: 0x62, + sourceNames: 0x6a, + destNames: 0x6b, + extendedSourceNames: 0xea, + extendedDestNames: 0xeb, + } +} \ No newline at end of file diff --git a/src/crosspoints.js b/src/crosspoints.js index 59f3dcd..566bf99 100644 --- a/src/crosspoints.js +++ b/src/crosspoints.js @@ -1,5 +1,6 @@ +import { cmd } from './consts.js' -export function crosspointConnected (data) { +export function crosspointConnected(data) { //const matrix = ((data[1] & 0xf0) >> 4) + 1 unused const level = (data[1] & 0x0f) + 1 const destDiv = (data[2] & 0x70) >> 4 @@ -13,7 +14,7 @@ export function crosspointConnected (data) { this.update_crosspoints(source, dest, level) } -export function ext_crosspointConnected (data) { +export function ext_crosspointConnected(data) { //const matrix = data[1] + 1 const level = data[2] + 1 const destDiv = data[3] * 256 @@ -29,10 +30,10 @@ export function ext_crosspointConnected (data) { this.update_crosspoints(source, dest, level) } -export function update_crosspoints (source, dest, level) { +export function update_crosspoints(source, dest, level) { if (dest == this.selected_dest) { // update variables for selected dest source - this.setVariableValues({[`Sel_Dest_Source_Level_${level.toString()}`]: source}) + this.setVariableValues({ [`Sel_Dest_Source_Level_${level.toString()}`]: source }) if (this.source_names.length > 0) { // only if names have been retrieved try { @@ -63,8 +64,8 @@ export function update_crosspoints (source, dest, level) { this.checkFeedbacks('source_dest_route') } -export function SetCrosspoint (sourceN, destN, levelN) { - let action +export function SetCrosspoint(sourceN, destN, levelN) { + let action this.log('debug', 'Crosspoint ' + sourceN + '>' + destN + ' level ' + levelN) console.log('SetCrosspoint ' + sourceN + '>' + destN + ' level ' + levelN) @@ -84,7 +85,7 @@ export function SetCrosspoint (sourceN, destN, levelN) { } if (sourceN > 1024 || destN > 1024 || levelN > 16) { // Extended command required - const COM = '82' + const COM = cmd.extendedConnect // Matrix const matrix = this.padLeft((this.config.matrix - 1).toString(16), 2) // Level @@ -105,7 +106,7 @@ export function SetCrosspoint (sourceN, destN, levelN) { action = COM + matrix + level + destDIV + destMOD + sourceDIV + sourceMOD + count + checksum } else { // Standard Command - const COM = '02' + const COM = cmd.connect // Matrix and Level const matrix = (this.config.matrix - 1) << 4 const level = levelN - 1 @@ -129,7 +130,7 @@ export function SetCrosspoint (sourceN, destN, levelN) { this.sendMessage(action) } -export function getCrosspoints (destN) { +export function getCrosspoints(destN) { console.log('GetCrosspoint ' + destN) if (destN <= 0 || destN > 65536) { @@ -139,7 +140,7 @@ export function getCrosspoints (destN) { if (this.config.max_levels > 16 || destN > 1024) { // Extended commands - const COM = '81' + const COM = cmd.extendedinterrogate // Byte count const count = '05' // Matrix @@ -160,7 +161,7 @@ export function getCrosspoints (destN) { } } else { // Standard commands - const COM = '01' + const COM = cmd.interrogate // Byte count const count = '04' // Matrix and Level @@ -181,4 +182,4 @@ export function getCrosspoints (destN) { this.sendMessage(action) } } -} \ No newline at end of file +} diff --git a/src/decode.js b/src/decode.js index 1924300..062e2cb 100644 --- a/src/decode.js +++ b/src/decode.js @@ -1,37 +1,39 @@ +import { hexBytes } from './consts.js' + export function decode (data) { let message = [] if (data.length > 0) { for (let j = 0; j < data.length; j++) { - if (data[j] == 0x10) { + if (data[j] == hexBytes.DLE) { switch (data[j + 1]) { - case 0x02: + case hexBytes.STX: console.log('Received SOM') j++ continue //break - case 0x03: + case hexBytes.ETX: console.log('Received EOM') j++ continue //break - case 0x06: + case hexBytes.ACK: console.log('Received ACK') j++ continue //break - case 0x10: + case hexBytes.DLE: // remove repeated byte 0x10 message.push(data[j]) j++ continue //break - case 0x15: + case hexBytes.NAK: console.log('Received NAK') j++ continue @@ -53,19 +55,19 @@ export function decode (data) { //let responses switch (message[0]) { // Command - case 0x03: - case 0x04: + case hexBytes.cmd.tally: + case hexBytes.cmd.connected: // Crosspoint Tally, Crosspoint Connected this.crosspointConnected(message) break - case 0x83: - case 0x84: + case hexBytes.cmd.extendedTally: + case hexBytes.cmd.extendedConnected: // Extended Crosspoint Connected this.ext_crosspointConnected(message) break - case 0x62: + case hexBytes.cmd.protocolImplementation: // Protocol Implementation Response //requests = message[1] //responses = message[2] @@ -84,19 +86,19 @@ export function decode (data) { } break - case 0x6a: - case 0x6b: + case hexBytes.cmd.sourceNames: + case hexBytes.cmd.destNames: // Standard Names Request Reply this.processLabels(message) break - case 0xea: + case hexBytes.cmd.extendedSourceNames: // Extended Source Names Reply // Allows for extra Level field in response this.ext_processSourceLabels(message) break - case 0xeb: + case hexBytes.cmd.extendedDestNames: // Extended Destination Names Reply // There is no difference in structure to the standard response this.processLabels(message) diff --git a/src/keepalive.js b/src/keepalive.js index 4d5d43e..bcc0350 100644 --- a/src/keepalive.js +++ b/src/keepalive.js @@ -1,4 +1,4 @@ -const keepAliveTimeOut = 30000 +import { DLE, STX, ETX, keepAliveTimeOut } from './consts.js' export function startKeepAliveTimer(){ if (this.keepAliveTimer) { @@ -17,5 +17,6 @@ export function stopKeepAliveTimer(){ } export function keepAlive(){ - //this.sendMessage('61019E') what message to send here? + this.socket.send(this.hexStringToBuffer(DLE + STX + '0000' + DLE + ETX)) + this.startKeepAliveTimer() } diff --git a/src/names.js b/src/names.js index 68b58c2..ec146e2 100644 --- a/src/names.js +++ b/src/names.js @@ -1,19 +1,21 @@ +import { cmd } from './consts.js' + export function readNames() { // reset this.source_names = [] this.dest_names = [] - this.setVariableValues({ Sources: 0, Destinations : 0}) - let get_source - let get_dest + this.setVariableValues({ Sources: 0, Destinations: 0 }) + let get_source + let get_dest if (this.config.extended_support === true) { // extended commands (only gets source names for level 1) var matrix = this.padLeft((this.config.matrix - 1).toString(16), 2) - get_source = 'E4' + matrix + '00' + this.config.name_chars + '04' - get_dest = 'E6' + matrix + this.config.name_chars + '03' + get_source = cmd.extendedGetSourceName + matrix + '00' + this.config.name_chars + '04' + get_dest = cmd.extendedGetDestName + matrix + this.config.name_chars + '03' } else { // standard commands - get_source = '64' + this.config.name_chars + '02' - get_dest = '66' + this.config.name_chars + '02' + get_source = cmd.getSourceName + this.config.name_chars + '02' + get_dest = cmd.getDestName + this.config.name_chars + '02' } // get source names diff --git a/src/presets.js b/src/presets.js index 17e888a..74062eb 100644 --- a/src/presets.js +++ b/src/presets.js @@ -1,17 +1,15 @@ -import { colours } from './consts.js' +import { colours, presetDefaults } from './consts.js' export async function UpdatePresets(self) { let presets = [] - presets.push({ + presets['take']={ category: 'Actions', type: 'button', name: 'Take', style: { - style: 'text', + ...presetDefaults.style, text: 'Take', - size: '18', - color: colours.white, bgcolor: colours.red, }, steps: [{ @@ -19,43 +17,41 @@ export async function UpdatePresets(self) { action: 'take', }, }], - }) + } - presets.push({ + presets['refresh'] = { category: 'Actions', type: 'button', name: 'Refresh Names', style: { - style: 'text', + ...presetDefaults.style, text: 'Refresh Names', - size: '18', - color: colours.white, - bgcolor: colours.black, }, - actions: [ + steps: [ { - action: 'get_names', + down: { + action: 'get_names', + }, }, ], - }) + } for (let i = 1; i <= 32; i++) { - presets.push({ + presets[`source_number_${i}`] = { category: 'Sources (by number)', type: 'button', name: 'Source ' + i, style: { - style: 'text', + ...presetDefaults.style, text: 'S' + i, - size: '18', - color: colours.white, - bgcolor: colours.black, }, - actions: [ + steps: [ { - action: 'select_source', - options: { - source: i, + down: { + action: 'select_source', + options: { + source: i, + }, }, }, ], @@ -71,24 +67,23 @@ export async function UpdatePresets(self) { }, }, ], - }) + } - presets.push({ + presets[`destination_number_${i}`] = { category: 'Destinations (by number)', type: 'button', name: 'Destination ' + i, style: { - style: 'text', + ...presetDefaults.style, text: 'D' + i, - size: '18', - color: colours.white, - bgcolor: colours.black, }, - actions: [ + steps: [ { - action: 'select_dest', - options: { - dest: i, + down: { + action: 'select_dest', + options: { + dest: i, + }, }, }, ], @@ -104,7 +99,7 @@ export async function UpdatePresets(self) { }, }, ], - }) + } } self.setPresetDefinitions(presets) } diff --git a/src/tcp.js b/src/tcp.js index 7f91fe2..673eccf 100644 --- a/src/tcp.js +++ b/src/tcp.js @@ -1,12 +1,11 @@ import { InstanceStatus, TCPHelper } from '@companion-module/base' import { Buffer } from 'node:buffer' -import { DLE, STX, ETX } from './consts.js' -//import { msgDelay } from './consts.js' +import { ACK, DLE, STX, ETX } from './consts.js' export function sendAck() { this.log('debug','Sending ACK') if (this.socket !== undefined && this.socket.connected) { - this.socket.send(this.hexStringToBuffer('1006')) + this.socket.send(this.hexStringToBuffer(DLE + ACK)) this.startKeepAliveTimer() } else { this.log('warn','Socket not connected :(') @@ -43,8 +42,8 @@ export function sendMessage(message) { let packed = '' for (let j = 0; j < message.length; j = j + 2) { let b = message.substr(j, 2) - if (b === '10') { - packed = packed + '1010' + if (b === DLE) { + packed = packed + DLE + DLE } else { packed = packed + b } From be8b5910b2b04ed6076d343e2f8d3c46417d31a3 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 18:17:33 +1000 Subject: [PATCH 11/25] setupVariables during init & configUpdated --- src/config.js | 1 + src/index.js | 1 + 2 files changed, 2 insertions(+) diff --git a/src/config.js b/src/config.js index 4ecc7ef..d54c87a 100644 --- a/src/config.js +++ b/src/config.js @@ -4,6 +4,7 @@ export async function configUpdated(config) { this.log('debug','update config') this.updateStatus(InstanceStatus.Connecting) this.config = config + this.setupVariables() this.updateVariableDefinitions() this.updateFeedbacks() this.updateActions() diff --git a/src/index.js b/src/index.js index 1fa2821..b191a65 100644 --- a/src/index.js +++ b/src/index.js @@ -33,6 +33,7 @@ class SW_P_08 extends InstanceBase { async init(config){ this.updateStatus(InstanceStatus.Connecting) this.config = config + this.setupVariables() this.updateVariableDefinitions() this.updateFeedbacks() this.updateActions() From 63ad7b8648b78860527d13c35140a38b94bfab30 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 20:37:00 +1000 Subject: [PATCH 12/25] fixups prettier --- companion/HELP.md | 3 ++- src/actions.js | 2 +- src/config.js | 15 +-------------- src/index.js | 29 +++++++++++++++++++++++++---- src/keepalive.js | 6 ++++-- src/labels.js | 15 +++++++-------- src/tcp.js | 22 +++++++++++----------- src/upgrades.js | 7 +++++++ src/variables.js | 36 ++++++++++++++++++++---------------- 9 files changed, 78 insertions(+), 57 deletions(-) diff --git a/companion/HELP.md b/companion/HELP.md index 3022e22..0dcb767 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -84,4 +84,5 @@ Some dynamic information is stored in variables which you can access through the ## Version 2.0.0 - Update for Companion 3 -- Add Connection Keep Alive \ No newline at end of file +- Add Connection Keep Alive +- Accept variables for Select Source Name, Select Destination Name, Set Crosspoint by Name \ No newline at end of file diff --git a/src/actions.js b/src/actions.js index 9157951..a258aa5 100644 --- a/src/actions.js +++ b/src/actions.js @@ -164,7 +164,7 @@ export async function UpdateActions(self) { actionDefinitions['set_crosspoint_name'] = { name: 'Set crosspoint by name', options: [ - { ...actionOptions.levels, choices: this.levels }, + { ...actionOptions.levels, choices: self.levels }, { ...actionOptions.sourceName, choices: self.source_names }, { ...actionOptions.destinationName, choices: self.dest_names }, ], diff --git a/src/config.js b/src/config.js index d54c87a..d3c133f 100644 --- a/src/config.js +++ b/src/config.js @@ -1,17 +1,4 @@ -import { InstanceStatus, Regex } from '@companion-module/base' - -export async function configUpdated(config) { - this.log('debug','update config') - this.updateStatus(InstanceStatus.Connecting) - this.config = config - this.setupVariables() - this.updateVariableDefinitions() - this.updateFeedbacks() - this.updateActions() - this.initPresets() - this.init_tcp() - this.checkFeedbacks('selected_level', 'selected_level_dest','selected_dest','selected_source') -} +import { Regex } from '@companion-module/base' export function getConfigFields() { return [ diff --git a/src/index.js b/src/index.js index b191a65..cb6ecf4 100644 --- a/src/index.js +++ b/src/index.js @@ -27,17 +27,38 @@ import * as util from './util.js' class SW_P_08 extends InstanceBase { constructor(internal) { super(internal) - Object.assign(this, { ...config, ...crosspoints, ...decode, ...keepalive, ...labels, ...levels, ...names, ...tcp, ...util }) + Object.assign(this, { + ...config, + ...crosspoints, + ...decode, + ...keepalive, + ...labels, + ...levels, + ...names, + ...tcp, + ...util, + }) } - async init(config){ + async init(config) { this.updateStatus(InstanceStatus.Connecting) this.config = config this.setupVariables() - this.updateVariableDefinitions() this.updateFeedbacks() this.updateActions() - this.initPresets() + this.updatePresets() + this.init_tcp() + this.checkFeedbacks('selected_level', 'selected_level_dest', 'selected_dest', 'selected_source') + } + + async configUpdated(config) { + this.log('debug', 'update config') + this.updateStatus(InstanceStatus.Connecting) + this.config = config + this.setupVariables() + this.updateFeedbacks() + this.updateActions() + this.updatePresets() this.init_tcp() this.checkFeedbacks('selected_level', 'selected_level_dest', 'selected_dest', 'selected_source') } diff --git a/src/keepalive.js b/src/keepalive.js index bcc0350..8ec9ed1 100644 --- a/src/keepalive.js +++ b/src/keepalive.js @@ -17,6 +17,8 @@ export function stopKeepAliveTimer(){ } export function keepAlive(){ - this.socket.send(this.hexStringToBuffer(DLE + STX + '0000' + DLE + ETX)) - this.startKeepAliveTimer() + if (this.socket !== undefined && this.socket.isConnected) { + this.socket.send(this.hexStringToBuffer(DLE + STX + '0000' + DLE + ETX)) + this.startKeepAliveTimer() + } } diff --git a/src/labels.js b/src/labels.js index 704bd54..cbb4560 100644 --- a/src/labels.js +++ b/src/labels.js @@ -1,6 +1,6 @@ +import { hexBytes } from './consts.js' - -export function processLabels (data) { +export function processLabels(data) { const char_length_table = [4, 8, 12] // byte1 = matrix (& level for sources) @@ -12,7 +12,7 @@ export function processLabels (data) { this.extractLabels(data, char_length, label_number, labels_in_part, start) } -export function ext_processSourceLabels (data) { +export function ext_processSourceLabels(data) { const char_length_table = [4, 8, 12] // byte1 = matrix number @@ -25,8 +25,7 @@ export function ext_processSourceLabels (data) { this.extractLabels(data, char_length, label_number, labels_in_part, start) } -export function extractLabels (data, char_length, label_number, labels_in_part, s) { - +export function extractLabels(data, char_length, label_number, labels_in_part, s) { let l = 0 console.log('label chars:' + char_length) @@ -43,13 +42,13 @@ export function extractLabels (data, char_length, label_number, labels_in_part, l = l + 1 label_number = label_number + 1 - if (data[0] == 0x6a || data[0] == 0xea) { + if (data[0] == hexBytes.cmd.sourceNames || data[0] == hexBytes.cmd.extendedSourceNames) { // sources this.source_names.splice(label_number - 1, 0, { id: label_number, label: label_number.toString() + ': ' + label.trim(), }) - } else if (data[0] == 0x6b || data[0] == 0xeb) { + } else if (data[0] == hexBytes.cmd.destNames || data[0] == hexBytes.cmd.extendedDestNames) { // destinations this.dest_names.splice(label_number - 1, 0, { id: label_number, @@ -74,4 +73,4 @@ export function extractLabels (data, char_length, label_number, labels_in_part, // update dropdown lists this.updateActions() -} \ No newline at end of file +} diff --git a/src/tcp.js b/src/tcp.js index 673eccf..56d8e88 100644 --- a/src/tcp.js +++ b/src/tcp.js @@ -4,11 +4,11 @@ import { ACK, DLE, STX, ETX } from './consts.js' export function sendAck() { this.log('debug','Sending ACK') - if (this.socket !== undefined && this.socket.connected) { + if (this.socket !== undefined && this.socket.isConnected) { this.socket.send(this.hexStringToBuffer(DLE + ACK)) this.startKeepAliveTimer() } else { - this.log('warn','Socket not connected :(') + this.log('warn', 'Socket not connected :(') } } @@ -54,7 +54,7 @@ export function sendMessage(message) { console.log('Sending >> ' + cmd) if (cmd !== undefined) { - if (this.socket !== undefined && this.socket.connected) { + if (this.socket !== undefined && this.socket.isConnected) { this.socket.send(this.hexStringToBuffer(cmd)) this.startKeepAliveTimer() } else { @@ -62,8 +62,8 @@ export function sendMessage(message) { } } } - -export async function init_tcp () { + +export function init_tcp() { let receivebuffer = Buffer.from('') if (this.socket !== undefined) { @@ -74,18 +74,18 @@ export async function init_tcp () { if (this.config.host) { this.socket = new TCPHelper(this.config.host, this.config.port) - this.socket.on('status_change', function (status, message) { + this.socket.on('status_change', (status, message) => { this.updateStatus(status, message) }) - this.socket.on('error', function (err) { + this.socket.on('error', (err) => { this.log('error', 'Network error: ' + err.message) - this.updateStatus(InstanceStatus.ConnectionFailure, err.message) + this.updateStatus(InstanceStatus.ConnectionFailure, err.message) this.stopKeepAliveTimer() }) - this.socket.on('connect', function () { - this.updateStatus(InstanceStatus.Ok, 'Connected') + this.socket.on('connect', () => { + this.updateStatus(InstanceStatus.Ok, 'Connected') this.startKeepAliveTimer() if (this.config.supported_commands_on_connect === true) { // request protocol implementation @@ -93,7 +93,7 @@ export async function init_tcp () { } }) - this.socket.on('data', function (chunk) { + this.socket.on('data', (chunk) => { if (Buffer.compare(chunk, receivebuffer) != 0) { // console.log('Received: ' + chunk.length + ' bytes ', chunk.toString('hex').match(/../g).join(' ')) // send ACK diff --git a/src/upgrades.js b/src/upgrades.js index e69de29..a7fff1b 100644 --- a/src/upgrades.js +++ b/src/upgrades.js @@ -0,0 +1,7 @@ +export const UpgradeScripts = [ + /* + * Place your upgrade scripts here + * Remember that once it has been added it cannot be removed! + */ + +] diff --git a/src/variables.js b/src/variables.js index 49e6075..2180c2e 100644 --- a/src/variables.js +++ b/src/variables.js @@ -1,5 +1,6 @@ export async function SetupVariables(self) { // Implemented Commands + let varList = [] self.commands = [] // Hold values @@ -18,8 +19,8 @@ export async function SetupVariables(self) { self.selected_level.push({ id: i, enabled: true }) } - self.debug(self.levels) - self.debug(self.selected_level) + //console.log(self.levels) + //console.log(self.selected_level) // Labels self.source_names = [] @@ -27,16 +28,19 @@ export async function SetupVariables(self) { self.updateVariableDefinitions() - self.setVariable('Sources', 0) - self.setVariable('Destinations', 0) + varList['Sources'] = 0 + varList['Destinations'] = 0 - self.setVariable('Source', self.selected_source) - self.setVariable('Destination', self.selected_dest) + varList['Source'] = self.selected_source + varList['Destination'] = self.selected_dest + self.setVariableValues(varList) } export async function UpdateVariableDefinitions(self) { let coreVariables = [] - + const sourceKeys = Object.keys(self.source_names) + const destKeys = Object.keys(self.dest_names) + coreVariables.push( { name : 'Number of source names returned by router', @@ -66,31 +70,31 @@ export async function UpdateVariableDefinitions(self) { variableId: 'Sel_Dest_Source_Name_Level_' + i.toString(), }) } - - for (let i = 1; i <= Object.keys(self.source_names).length; i++) { + + for (let i = 1; i <= sourceKeys.length; i++) { coreVariables.push({ - label: 'Source ' + i.toString(), + name: 'Source ' + i.toString(), variableId: 'Source_' + i.toString(), }) } - - for (let i = 1; i <= Object.keys(self.dest_names).length; i++) { + + for (let i = 1; i <= destKeys.length; i++) { coreVariables.push({ - label: 'Destination ' + i.toString(), + name: 'Destination ' + i.toString(), variableId: 'Destination_' + i.toString(), }) } self.setVariableDefinitions(coreVariables) - let labelDump = {} + let labelDump = [] - for (let i = 0; i < Object.keys(self.source_names).length; i++) { + for (let i = 0; i < sourceKeys.length; i++) { //let variableValue = self.stripNumber(self.source_names[i].label) not used labelDump[`Source_${self.source_names[i].id}`] = self.stripNumber(self.source_names[i].label) } - for (let i = 0; i < Object.keys(self.dest_names).length; i++) { + for (let i = 0; i < destKeys.length; i++) { labelDump[`Destination_${self.dest_names[i].id}`] = self.stripNumber(self.dest_names[i].label) } From 6004209a57528cc463a9abcfa4f65dd61d8fae5e Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 20:50:38 +1000 Subject: [PATCH 13/25] remove redundant comments add crosspoint connected, crosspoint connect by name feedbacks --- companion/HELP.md | 3 ++- src/consts.js | 16 ++++++++++++++ src/feedbacks.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++- src/labels.js | 2 -- src/tcp.js | 1 - src/variables.js | 7 +----- 6 files changed, 73 insertions(+), 11 deletions(-) diff --git a/companion/HELP.md b/companion/HELP.md index 0dcb767..f0f0bfa 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -85,4 +85,5 @@ Some dynamic information is stored in variables which you can access through the ## Version 2.0.0 - Update for Companion 3 - Add Connection Keep Alive -- Accept variables for Select Source Name, Select Destination Name, Set Crosspoint by Name \ No newline at end of file +- Accept variables for Select Source Name, Select Destination Name, Set Crosspoint by Name +- Add Crosspoint Connected, Crosspoint Connected By Name feedback \ No newline at end of file diff --git a/src/consts.js b/src/consts.js index b6408c1..feb618e 100644 --- a/src/consts.js +++ b/src/consts.js @@ -33,6 +33,14 @@ export const feedbackOptions = { min: 1, max: 65536, }, + destinationName: { + type: 'dropdown', + label: 'Destination', + id: 'dest', + default: 1, + allowCustom: true, + tooltip: 'Accepts Variable. Should return an integer between 1 & 65536', + }, source: { type: 'number', label: 'Source', @@ -41,6 +49,14 @@ export const feedbackOptions = { min: 1, max: 65536, }, + sourceName: { + type: 'dropdown', + label: 'Source', + id: 'source', + default: 1, + allowCustom: true, + tooltip: 'Accepts Variable. Should return an integer between 1 & 65536', + }, } export const actionOptions = { diff --git a/src/feedbacks.js b/src/feedbacks.js index 1797a43..a2c6234 100644 --- a/src/feedbacks.js +++ b/src/feedbacks.js @@ -1,7 +1,6 @@ import { colours, feedbackOptions } from './consts.js' export async function UpdateFeedbacks(self) { - // feedback let feedbacks = {} feedbacks['selected_level'] = { @@ -128,5 +127,59 @@ export async function UpdateFeedbacks(self) { }, } + feedbacks['crosspoint_connected'] = { + type: 'boolean', + label: 'Crosspoint Connected', + description: 'Change button colour when this crosspoint is connected on any level', + style: { + color: colours.black, + bgcolor: colours.orange, + }, + options: [feedbackOptions.source, feedbackOptions.destination], + callback: async (feedback) => { + // look for this dest in route table + console.log('dest:source feedback ' + feedback.options.dest + ':' + feedback.options.source) + for (let i = 0; i < self.routeTable.length; i++) { + if (self.routeTable[i].dest === feedback.options.dest) { + if (self.routeTable[i].source === feedback.options.source) { + return true + } + } + } + return false + }, + } + feedbacks['crosspoint_connected_by_name'] = { + type: 'boolean', + label: 'Crosspoint Connected By Name', + description: 'Change button colour when this crosspoint is connected on any level', + style: { + color: colours.black, + bgcolor: colours.orange, + }, + options: [feedbackOptions.sourceName, feedbackOptions.destinationName], + callback: async (feedback, context) => { + const source = parseInt(await context.parseVariablesInString(feedback.options.source)) + const dest = parseInt(await context.parseVariablesInString(feedback.options.dest)) + // look for this dest in route table + if (isNaN(source) || source < 1 || source > 65536 || isNaN(dest) || dest < 1 || dest > 65536) { + self.log( + 'warn', + `crosspoint_connected_by_name has been passed an out of range variable - src ${source} : dst ${dest}` + ) + return undefined + } + console.log('dest:source feedback ' + feedback.options.dest + ':' + feedback.options.source) + for (let i = 0; i < self.routeTable.length; i++) { + if (self.routeTable[i].dest === dest) { + if (self.routeTable[i].source === source) { + return true + } + } + } + return false + }, + } + self.setFeedbackDefinitions(feedbacks) } diff --git a/src/labels.js b/src/labels.js index cbb4560..909b4e3 100644 --- a/src/labels.js +++ b/src/labels.js @@ -56,7 +56,6 @@ export function extractLabels(data, char_length, label_number, labels_in_part, s }) } - // console.log('label ' + this.padLeft(label_number,2) + ' |' + label + '|') // this.log('debug','label ' + this.padLeft(label_number,2) + ' |' + label + '|') } @@ -65,7 +64,6 @@ export function extractLabels(data, char_length, label_number, labels_in_part, s Destinations: Object.keys(this.dest_names).length, }) - // need to find a way of only calling these functions on the last part of the labels this.updateVariableDefinitions() console.log(this.source_names) diff --git a/src/tcp.js b/src/tcp.js index 56d8e88..cf9f408 100644 --- a/src/tcp.js +++ b/src/tcp.js @@ -13,7 +13,6 @@ export function sendAck() { } export function sendMessage(message) { - // minimum length is 1 byte if (message.length < 2) { this.log('warn', 'Empty or invalid message!') return diff --git a/src/variables.js b/src/variables.js index 2180c2e..a575b77 100644 --- a/src/variables.js +++ b/src/variables.js @@ -19,9 +19,6 @@ export async function SetupVariables(self) { self.selected_level.push({ id: i, enabled: true }) } - //console.log(self.levels) - //console.log(self.selected_level) - // Labels self.source_names = [] self.dest_names = [] @@ -40,7 +37,7 @@ export async function UpdateVariableDefinitions(self) { let coreVariables = [] const sourceKeys = Object.keys(self.source_names) const destKeys = Object.keys(self.dest_names) - + coreVariables.push( { name : 'Number of source names returned by router', @@ -90,7 +87,6 @@ export async function UpdateVariableDefinitions(self) { let labelDump = [] for (let i = 0; i < sourceKeys.length; i++) { - //let variableValue = self.stripNumber(self.source_names[i].label) not used labelDump[`Source_${self.source_names[i].id}`] = self.stripNumber(self.source_names[i].label) } @@ -98,6 +94,5 @@ export async function UpdateVariableDefinitions(self) { labelDump[`Destination_${self.dest_names[i].id}`] = self.stripNumber(self.dest_names[i].label) } - // console.log(labelDump) self.setVariableValues(labelDump) } \ No newline at end of file From 1b9cd26b1d4a4ff91533151e287bc2100d04df4f Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 21:44:49 +1000 Subject: [PATCH 14/25] fix feedbacks fix presets on extractLabels updateFeedbacks --- src/feedbacks.js | 68 ++++++++++++++++++++++++++---------------------- src/labels.js | 1 + src/presets.js | 56 +++++++++++++++++++++++++-------------- 3 files changed, 74 insertions(+), 51 deletions(-) diff --git a/src/feedbacks.js b/src/feedbacks.js index a2c6234..afd7ce3 100644 --- a/src/feedbacks.js +++ b/src/feedbacks.js @@ -1,13 +1,13 @@ import { colours, feedbackOptions } from './consts.js' export async function UpdateFeedbacks(self) { - let feedbacks = {} + let feedbackDefinitions = [] - feedbacks['selected_level'] = { + feedbackDefinitions['selected_level'] = { + name: 'Selected Levels', type: 'boolean', - label: 'Selected Levels', description: 'Change colour of button on selected levels', - style: { + defaultStyle: { color: colours.black, bgcolor: colours.purple, }, @@ -32,18 +32,15 @@ export async function UpdateFeedbacks(self) { }, } - feedbacks['selected_level_dest'] = { + feedbackDefinitions['selected_level_dest'] = { + name: 'Selected Levels and Destination', type: 'boolean', - label: 'Selected Levels and Destination', description: 'Change colour of button on selected levels and destination', - style: { + defaultStyle: { color: colours.black, bgcolor: colours.purple, }, - options: [ - { ...feedbackOptions.levels, choices: self.levels }, - feedbackOptions.destination, - ], + options: [{ ...feedbackOptions.levels, choices: self.levels }, feedbackOptions.destination], callback: async (feedback) => { if (self.selected_dest === feedback.options.dest) { let l = feedback.options.level.length @@ -68,15 +65,15 @@ export async function UpdateFeedbacks(self) { }, } - feedbacks['selected_dest'] = { + feedbackDefinitions['selected_dest'] = { type: 'boolean', - label: 'Selected Destination', + name: 'Selected Destination', description: 'Change colour of button on selected destination', - style: { + defaultStyle: { color: colours.black, bgcolor: colours.green, }, - options: [ feedbackOptions.destination ], + options: [feedbackOptions.destination], callback: async (feedback) => { if (self.selected_dest === feedback.options.dest) { return true @@ -86,15 +83,15 @@ export async function UpdateFeedbacks(self) { }, } - feedbacks['selected_source'] = { + feedbackDefinitions['selected_source'] = { type: 'boolean', - label: 'Selected Source', + name: 'Selected Source', description: 'Change colour of button on selected source', - style: { + defaultStyle: { color: colours.black, bgcolor: colours.cyan, }, - options: [ feedbackOptions.source ], + options: [feedbackOptions.source], callback: async (feedback) => { if (self.selected_source === feedback.options.source) { return true @@ -104,15 +101,15 @@ export async function UpdateFeedbacks(self) { }, } - feedbacks['source_dest_route'] = { + feedbackDefinitions['source_dest_route'] = { type: 'boolean', - label: 'Source Routed to Destination', + name: 'Source Routed to Destination', description: 'Change button colour when this source is routed to selected destination on any level', - style: { + defaultStyle: { color: colours.black, bgcolor: colours.orange, }, - options: [ feedbackOptions.source ], + options: [feedbackOptions.source], callback: async (feedback) => { // look for this dest in route table console.log('dest:source feedback ' + self.selected_dest + ':' + feedback.options.source) @@ -127,11 +124,11 @@ export async function UpdateFeedbacks(self) { }, } - feedbacks['crosspoint_connected'] = { + feedbackDefinitions['crosspoint_connected'] = { type: 'boolean', - label: 'Crosspoint Connected', + name: 'Crosspoint Connected', description: 'Change button colour when this crosspoint is connected on any level', - style: { + defaultStyle: { color: colours.black, bgcolor: colours.orange, }, @@ -149,15 +146,24 @@ export async function UpdateFeedbacks(self) { return false }, } - feedbacks['crosspoint_connected_by_name'] = { + feedbackDefinitions['crosspoint_connected_by_name'] = { type: 'boolean', - label: 'Crosspoint Connected By Name', + name: 'Crosspoint Connected By Name', description: 'Change button colour when this crosspoint is connected on any level', - style: { + defaultStyle: { color: colours.black, bgcolor: colours.orange, }, - options: [feedbackOptions.sourceName, feedbackOptions.destinationName], + options: [ + { + ...feedbackOptions.sourceName, + choices: self.source_names, + }, + { + ...feedbackOptions.destinationName, + choices: self.dest_names, + }, + ], callback: async (feedback, context) => { const source = parseInt(await context.parseVariablesInString(feedback.options.source)) const dest = parseInt(await context.parseVariablesInString(feedback.options.dest)) @@ -181,5 +187,5 @@ export async function UpdateFeedbacks(self) { }, } - self.setFeedbackDefinitions(feedbacks) + self.setFeedbackDefinitions(feedbackDefinitions) } diff --git a/src/labels.js b/src/labels.js index 909b4e3..60b3d0c 100644 --- a/src/labels.js +++ b/src/labels.js @@ -71,4 +71,5 @@ export function extractLabels(data, char_length, label_number, labels_in_part, s // update dropdown lists this.updateActions() + this.updateFeedbacks() } diff --git a/src/presets.js b/src/presets.js index 74062eb..0a304e1 100644 --- a/src/presets.js +++ b/src/presets.js @@ -3,7 +3,7 @@ import { colours, presetDefaults } from './consts.js' export async function UpdatePresets(self) { let presets = [] - presets['take']={ + presets['take'] = { category: 'Actions', type: 'button', name: 'Take', @@ -12,11 +12,16 @@ export async function UpdatePresets(self) { text: 'Take', bgcolor: colours.red, }, - steps: [{ - down:{ - action: 'take', + steps: [ + { + down: [ + { + actionId: 'take', + delay: 0, + }, + ], }, - }], + ], } presets['refresh'] = { @@ -29,9 +34,12 @@ export async function UpdatePresets(self) { }, steps: [ { - down: { - action: 'get_names', - }, + down: [ + { + actionId: 'get_names', + delay: 0, + }, + ], }, ], } @@ -47,17 +55,20 @@ export async function UpdatePresets(self) { }, steps: [ { - down: { - action: 'select_source', - options: { - source: i, + down: [ + { + actionId: 'select_source', + options: { + source: i, + }, + delay: 0, }, - }, + ], }, ], feedbacks: [ { - type: 'selected_source', + feedbackId: 'selected_source', options: { source: i, }, @@ -65,6 +76,7 @@ export async function UpdatePresets(self) { color: colours.black, bgcolor: colours.cyan, }, + isInverted: false, }, ], } @@ -79,17 +91,20 @@ export async function UpdatePresets(self) { }, steps: [ { - down: { - action: 'select_dest', - options: { - dest: i, + down: [ + { + actionId: 'select_dest', + options: { + dest: i, + }, + delay: 0, }, - }, + ], }, ], feedbacks: [ { - type: 'selected_dest', + feedbackId: 'selected_dest', options: { dest: i, }, @@ -97,6 +112,7 @@ export async function UpdatePresets(self) { color: colours.black, bgcolor: colours.green, }, + isInverted: false, }, ], } From 76e67e9ca4c28f1ba6a8dcb1cc9134fc4cd6e08b Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 22:22:22 +1000 Subject: [PATCH 15/25] select source and select destination presets now generated according to router size select source preset now has source routed to destination feedback added --- companion/HELP.md | 5 ++- src/labels.js | 1 + src/presets.js | 100 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/companion/HELP.md b/companion/HELP.md index f0f0bfa..d259206 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -86,4 +86,7 @@ Some dynamic information is stored in variables which you can access through the - Update for Companion 3 - Add Connection Keep Alive - Accept variables for Select Source Name, Select Destination Name, Set Crosspoint by Name -- Add Crosspoint Connected, Crosspoint Connected By Name feedback \ No newline at end of file +- Add Crosspoint Connected, Crosspoint Connected By Name feedback +- Select Source and Destination Presets now generatered according to router size (up to 256) +- Select Source and Destination Presets now wtih names +- Select Source presets have 2nd Source Routed to Destination feedback \ No newline at end of file diff --git a/src/labels.js b/src/labels.js index 60b3d0c..96b5e13 100644 --- a/src/labels.js +++ b/src/labels.js @@ -72,4 +72,5 @@ export function extractLabels(data, char_length, label_number, labels_in_part, s // update dropdown lists this.updateActions() this.updateFeedbacks() + this.updatePresets() } diff --git a/src/presets.js b/src/presets.js index 0a304e1..efded94 100644 --- a/src/presets.js +++ b/src/presets.js @@ -44,7 +44,8 @@ export async function UpdatePresets(self) { ], } - for (let i = 1; i <= 32; i++) { + const srcLength = self.source_names.length > 256 ? 256 : self.source_names.length + for (let i = 1; i <= srcLength; i++) { presets[`source_number_${i}`] = { category: 'Sources (by number)', type: 'button', @@ -78,9 +79,69 @@ export async function UpdatePresets(self) { }, isInverted: false, }, + { + feedbackId: 'source_dest_route', + options: { + source: i, + }, + style: { + color: colours.black, + bgcolor: colours.red, + }, + isInverted: false, + }, ], } + presets[`source_name_${i}`] = { + category: 'Sources (by name)', + type: 'button', + name: `$(generic-module:Source_${i})`, + style: { + ...presetDefaults.style, + text: `$(generic-module:Source_${i})`, + }, + steps: [ + { + down: [ + { + actionId: 'select_source', + options: { + source: i, + }, + delay: 0, + }, + ], + }, + ], + feedbacks: [ + { + feedbackId: 'selected_source', + options: { + source: i, + }, + style: { + color: colours.black, + bgcolor: colours.cyan, + }, + isInverted: false, + }, + { + feedbackId: 'source_dest_route', + options: { + source: i, + }, + style: { + color: colours.black, + bgcolor: colours.red, + }, + isInverted: false, + }, + ], + } + } + const destLength = self.dest_names.length > 256 ? 256 : self.dest_names.length + for (let i = 1; i <= destLength; i++) { presets[`destination_number_${i}`] = { category: 'Destinations (by number)', type: 'button', @@ -116,6 +177,43 @@ export async function UpdatePresets(self) { }, ], } + + presets[`destination_name_${i}`] = { + category: 'Destinations (by name)', + type: 'button', + name: `$(generic-module:Destination_${i})`, + style: { + ...presetDefaults.style, + text: `$(generic-module:Destination_${i})`, + }, + steps: [ + { + down: [ + { + actionId: 'select_dest', + options: { + dest: i, + }, + delay: 0, + }, + ], + }, + ], + feedbacks: [ + { + feedbackId: 'selected_dest', + options: { + dest: i, + }, + style: { + color: colours.black, + bgcolor: colours.green, + }, + isInverted: false, + }, + ], + } } + self.setPresetDefinitions(presets) } From ae56bff1ed52ed643fd5ae3c522be219b64c7517 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 22:31:35 +1000 Subject: [PATCH 16/25] check all crosspoint related feedbacks at the end of update_crosspoints --- src/crosspoints.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/crosspoints.js b/src/crosspoints.js index 566bf99..9ef5031 100644 --- a/src/crosspoints.js +++ b/src/crosspoints.js @@ -52,7 +52,7 @@ export function update_crosspoints(source, dest, level) { // update existing this.routeTable[i].source = source console.log(this.routeTable) - this.checkFeedbacks('source_dest_route') + //this.checkFeedbacks('source_dest_route', 'crosspoint_connected', 'crosspoint_connected_by_name') return } } @@ -61,7 +61,7 @@ export function update_crosspoints(source, dest, level) { const new_route = { level: level, dest: dest, source: source } this.routeTable.push(new_route) console.log(this.routeTable) - this.checkFeedbacks('source_dest_route') + this.checkFeedbacks('source_dest_route', 'crosspoint_connected', 'crosspoint_connected_by_name') } export function SetCrosspoint(sourceN, destN, levelN) { From d296fadc312cdd74d93d2dcc68f42bbad048f75c Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 22:36:20 +1000 Subject: [PATCH 17/25] fix typos --- companion/HELP.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/companion/HELP.md b/companion/HELP.md index d259206..ba43aee 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -87,6 +87,6 @@ Some dynamic information is stored in variables which you can access through the - Add Connection Keep Alive - Accept variables for Select Source Name, Select Destination Name, Set Crosspoint by Name - Add Crosspoint Connected, Crosspoint Connected By Name feedback -- Select Source and Destination Presets now generatered according to router size (up to 256) -- Select Source and Destination Presets now wtih names +- Select Source and Destination Presets now generated according to router size (up to 256) +- Select Source and Destination Presets now with names - Select Source presets have 2nd Source Routed to Destination feedback \ No newline at end of file From 45aca4623da0acf80a21469d7ddf5e7072d684c8 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Mon, 8 Jul 2024 23:41:52 +1000 Subject: [PATCH 18/25] add actionRecorder support --- companion/HELP.md | 1 + src/crosspoints.js | 9 +++++++++ src/index.js | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/companion/HELP.md b/companion/HELP.md index ba43aee..4a8f9a8 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -87,6 +87,7 @@ Some dynamic information is stored in variables which you can access through the - Add Connection Keep Alive - Accept variables for Select Source Name, Select Destination Name, Set Crosspoint by Name - Add Crosspoint Connected, Crosspoint Connected By Name feedback +- Action Recorder support - Select Source and Destination Presets now generated according to router size (up to 256) - Select Source and Destination Presets now with names - Select Source presets have 2nd Source Routed to Destination feedback \ No newline at end of file diff --git a/src/crosspoints.js b/src/crosspoints.js index 9ef5031..cf853b3 100644 --- a/src/crosspoints.js +++ b/src/crosspoints.js @@ -62,6 +62,15 @@ export function update_crosspoints(source, dest, level) { this.routeTable.push(new_route) console.log(this.routeTable) this.checkFeedbacks('source_dest_route', 'crosspoint_connected', 'crosspoint_connected_by_name') + if (this.isRecordingActions) { + this.recordAction( + { + actionId: 'set_crosspoint', + options: { level: [level], source: source, dest: dest }, + }, + `connect dest ${dest} level ${level}` + ) + } } export function SetCrosspoint(sourceN, destN, levelN) { diff --git a/src/index.js b/src/index.js index cb6ecf4..c3fa78a 100644 --- a/src/index.js +++ b/src/index.js @@ -73,6 +73,11 @@ class SW_P_08 extends InstanceBase { this.updateStatus(InstanceStatus.Disconnected) } + // Track whether actions are being recorded + handleStartStopRecordActions(isRecording) { + this.isRecordingActions = isRecording + } + updateActions() { UpdateActions(this) } From 687bce8e1ad11342760e6485a96c3631a2096f15 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Tue, 9 Jul 2024 08:09:37 +1000 Subject: [PATCH 19/25] update help.md --- companion/HELP.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/companion/HELP.md b/companion/HELP.md index 4a8f9a8..424fa51 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -27,12 +27,14 @@ There are multiple ways of making crosspoint buttons to cater for different appl - **Route Source to selected Levels and Destination** and **Route Source name to selected Levels and Destination** Use the preset levels and destination with the source from this action and make the route - **Take** Make the crosspoint from preset levels, source and destination - **Clear** Forget any preset levels, destination or source. Optionally re-enable all levels. -- **Set Crosspoint** Specify levels, source and destination in the action and make the route +- **Set Crosspoint** and **Set Crosspoint by name** specify levels, source and destination in the action and make the route - **Refresh Source and Destination names** Ask the router for the current set of names and update ## Feedbacks Button background colours can be changed to show current selection status. +- **Crosspoiint Connected** +- **Crosspoiint Connected By Name** - **Selected Levels** - **Selected Levels and Destination** - **Selected Destination** @@ -50,10 +52,16 @@ Some dynamic information is stored in variables which you can access through the - **Source_?** The name of each source as defined in the the router - **Destination_?** The name of each destination as defined in the router -## Version 1.0.0 + +## Action Recorder +While recording, tally and connected messages recieved from the router will create new set crosspoint actions. Allowing for easy creation of salvos. + +## Version History + +### Version 1.0.0 - First Release -## Version 1.0.1 +### Version 1.0.1 - Reworked levels to be more flexible - Added route source by name action - Added toggle level action @@ -62,29 +70,29 @@ Some dynamic information is stored in variables which you can access through the - Added feedback for selected level and destination - Fixed packaging of bytes sent to router -## Version 1.0.2 +### Version 1.0.2 - Reworked incoming data processing - Reworked name decoding to support larger routers - Added module config option for name length - Added module config option to request names on connection - Added variables for selected destination source -## Version 1.0.3 +### Version 1.0.3 - Added module config option to disable the supported commands check -## Version 1.0.4 +### Version 1.0.4 - Added support for more than 16 levels - Added support for more then 1024 sources/destinations - Tidy up config page layout - Add more supported device types to the module properties -## Version 1.0.5 +### Version 1.0.5 - Added presets for some actions - Added feedback Source routed to selected Destination -## Version 2.0.0 -- Update for Companion 3 -- Add Connection Keep Alive +### Version 2.0.0 +- Update for Companion Version 3 +- Add connection keep alive - Accept variables for Select Source Name, Select Destination Name, Set Crosspoint by Name - Add Crosspoint Connected, Crosspoint Connected By Name feedback - Action Recorder support From 7a09123c086fabf4ec1170a13604c7057e3ecf8a Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Tue, 9 Jul 2024 09:13:40 +1000 Subject: [PATCH 20/25] minor refactor --- src/consts.js | 2 ++ src/names.js | 2 +- src/presets.js | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/consts.js b/src/consts.js index feb618e..ce7b83b 100644 --- a/src/consts.js +++ b/src/consts.js @@ -126,6 +126,8 @@ export const presetDefaults = { color: colours.white, bgcolor: colours.black, }, + sourceCount: 256, + destCount: 256, } export const cmd = { diff --git a/src/names.js b/src/names.js index ec146e2..2d4509c 100644 --- a/src/names.js +++ b/src/names.js @@ -9,7 +9,7 @@ export function readNames() { let get_dest if (this.config.extended_support === true) { // extended commands (only gets source names for level 1) - var matrix = this.padLeft((this.config.matrix - 1).toString(16), 2) + let matrix = this.padLeft((this.config.matrix - 1).toString(16), 2) get_source = cmd.extendedGetSourceName + matrix + '00' + this.config.name_chars + '04' get_dest = cmd.extendedGetDestName + matrix + this.config.name_chars + '03' } else { diff --git a/src/presets.js b/src/presets.js index efded94..f7a8ec2 100644 --- a/src/presets.js +++ b/src/presets.js @@ -44,7 +44,7 @@ export async function UpdatePresets(self) { ], } - const srcLength = self.source_names.length > 256 ? 256 : self.source_names.length + const srcLength = self.source_names.length > presetDefaults.sourceCount ? presetDefaults.sourceCount : self.source_names.length for (let i = 1; i <= srcLength; i++) { presets[`source_number_${i}`] = { category: 'Sources (by number)', @@ -140,7 +140,7 @@ export async function UpdatePresets(self) { ], } } - const destLength = self.dest_names.length > 256 ? 256 : self.dest_names.length + const destLength = self.dest_names.length > presetDefaults.destCount ? presetDefaults.destCount : self.dest_names.length for (let i = 1; i <= destLength; i++) { presets[`destination_number_${i}`] = { category: 'Destinations (by number)', From b2707c224a5fbbf6db1bf8abe01b0da63a8a385f Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Tue, 9 Jul 2024 09:18:38 +1000 Subject: [PATCH 21/25] maintain keepalive timer in case socket isnt connected --- src/keepalive.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/keepalive.js b/src/keepalive.js index 8ec9ed1..7c251c1 100644 --- a/src/keepalive.js +++ b/src/keepalive.js @@ -18,7 +18,8 @@ export function stopKeepAliveTimer(){ export function keepAlive(){ if (this.socket !== undefined && this.socket.isConnected) { + //Send dummy message this.socket.send(this.hexStringToBuffer(DLE + STX + '0000' + DLE + ETX)) - this.startKeepAliveTimer() } + this.startKeepAliveTimer() } From cd9f96d81d19b0c9bfcd232f1df6febd4af6046e Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Tue, 9 Jul 2024 14:00:56 +1000 Subject: [PATCH 22/25] remove redundant async actions & feedbacks refactor init (calls configUpdated, since run the same code). refactor record action feedback crosspoint_connected, crosspoiint_connected_by_name add subscribe subscribeActions and subscribeFeedbacks on connect --- src/actions.js | 41 ++++++++++++++++++++++++++--------------- src/crosspoints.js | 25 +++++++++++++++---------- src/feedbacks.js | 26 ++++++++++++++++++++------ src/index.js | 21 +++++++++------------ src/tcp.js | 5 ++++- 5 files changed, 74 insertions(+), 44 deletions(-) diff --git a/src/actions.js b/src/actions.js index a258aa5..70e5ed4 100644 --- a/src/actions.js +++ b/src/actions.js @@ -6,33 +6,36 @@ export async function UpdateActions(self) { actionDefinitions['select_level'] = { name: 'Select Levels', options: [{ ...actionOptions.levels, choices: self.levels }], - callback: async ({ options }) => { + callback: ({ options }) => { self.processLevelsSelection(options.level, true) }, } actionDefinitions['deselect_level'] = { name: 'De-Select Levels', options: [{ ...actionOptions.levels, choices: self.levels }], - callback: async ({ options }) => { + callback: ({ options }) => { self.processLevelsSelection(options.level, false) }, } actionDefinitions['toggle_level'] = { name: 'Toggle Levels', options: [{ ...actionOptions.levels, choices: self.levels }], - callback: async ({ options }) => { + callback: ({ options }) => { self.processLevelsSelection(options.level, 'toggle') }, } actionDefinitions['select_dest'] = { name: 'Select Destination', - options: [ actionOptions.destination ], - callback: async ({ options }) => { + options: [actionOptions.destination], + callback: ({ options }) => { self.selected_dest = parseInt(options.dest) self.getCrosspoints(options.dest) console.log('set destination ' + self.selected_dest) self.setVariableValues({ Destination: self.selected_dest }) - self.checkFeedbacks('selected_dest', 'selected_level_dest') + self.checkFeedbacks('selected_dest', 'selected_level_dest', 'source_dest_route') + }, + subscribe: (action) => { + self.getCrosspoints(action.options.dest) }, } actionDefinitions['select_dest_name'] = { @@ -48,13 +51,21 @@ export async function UpdateActions(self) { self.getCrosspoints(dest) console.log('set destination ' + self.selected_dest) self.setVariableValues({ Destination: self.selected_dest }) - self.checkFeedbacks('selected_dest', 'selected_level_dest') + self.checkFeedbacks('selected_dest', 'selected_level_dest', 'source_dest_route') + }, + subscribe: async (action) => { + const dest = parseInt(await self.parseVariablesInString(action.options.dest)) + if (isNaN(dest) || dest < 1 || dest > 65536) { + self.log('warn', `select_dest_name:Subscribe has been passed an out of range variable - dst ${dest}`) + return undefined + } + self.getCrosspoints(dest) }, } actionDefinitions['select_source'] = { name: 'Select Source', options: [ actionOptions.source ], - callback: async ({ options }) => { + callback: ({ options }) => { self.selected_source = parseInt(options.source) console.log('set source ' + self.selected_source) self.setVariableValues({ Source: self.selected_source }) @@ -79,7 +90,7 @@ export async function UpdateActions(self) { actionDefinitions['route_source'] = { name: 'Route Source to selected Levels and Destination', options: [ actionOptions.source ], - callback: async ({ options }) => { + callback: ({ options }) => { console.log(self.selected_level) const l = self.selected_level.length for (let i = 0; i < l; i++) { @@ -110,7 +121,7 @@ export async function UpdateActions(self) { actionDefinitions['take'] = { name: 'Take', options: [], - callback: async () => { + callback: () => { console.log(self.selected_level) const l = self.selected_level.length for (let i = 0; i < l; i++) { @@ -123,13 +134,13 @@ export async function UpdateActions(self) { actionDefinitions['clear'] = { name: 'Clear', options: [ actionOptions.clear, actionOptions.clearEnableLevels ], - callback: async ({ options }) => { + callback: ({ options }) => { if (options.clear === 'all' || options.clear === 'level') { self.selected_level = [] for (let i = 1; i <= self.config.max_levels; i++) { self.selected_level.push({ id: i, enabled: options.clear_enable_levels }) } - self.checkFeedbacks('selected_level', 'selected_level_dest') + self.checkFeedbacks('selected_level', 'selected_level_dest', 'source_dest_route') console.log('clear levels') console.log(self.selected_level) } @@ -137,7 +148,7 @@ export async function UpdateActions(self) { if (options.clear === 'all' || options.clear === 'dest') { self.selected_dest = 0 self.setVariableValues({ Destination: self.selected_dest }) - self.checkFeedbacks('selected_dest', 'selected_level_dest') + self.checkFeedbacks('selected_dest', 'selected_level_dest', 'source_dest_route') console.log('clear dest') } @@ -155,7 +166,7 @@ export async function UpdateActions(self) { actionOptions.source, actionOptions.destination, ], - callback: async ({ options }) => { + callback: ({ options }) => { for (let level_val of options.level) { self.SetCrosspoint(options.source, options.dest, level_val) } @@ -183,7 +194,7 @@ export async function UpdateActions(self) { actionDefinitions['get_names'] = { name: 'Refresh Source and Destination names', options: [], - callback: async () => { + callback: () => { self.readNames() }, } diff --git a/src/crosspoints.js b/src/crosspoints.js index cf853b3..455eb4f 100644 --- a/src/crosspoints.js +++ b/src/crosspoints.js @@ -30,6 +30,18 @@ export function ext_crosspointConnected(data) { this.update_crosspoints(source, dest, level) } +export function record_crosspoint(source, dest, level) { + if (this.isRecordingActions) { + this.recordAction( + { + actionId: 'set_crosspoint', + options: { level: [level], source: source, dest: dest }, + }, + `connect dest ${dest} level ${level}` + ) + } +} + export function update_crosspoints(source, dest, level) { if (dest == this.selected_dest) { // update variables for selected dest source @@ -52,7 +64,8 @@ export function update_crosspoints(source, dest, level) { // update existing this.routeTable[i].source = source console.log(this.routeTable) - //this.checkFeedbacks('source_dest_route', 'crosspoint_connected', 'crosspoint_connected_by_name') + this.checkFeedbacks('source_dest_route', 'crosspoint_connected', 'crosspoint_connected_by_name') + this.record_crosspoint(source, dest, level) return } } @@ -62,15 +75,7 @@ export function update_crosspoints(source, dest, level) { this.routeTable.push(new_route) console.log(this.routeTable) this.checkFeedbacks('source_dest_route', 'crosspoint_connected', 'crosspoint_connected_by_name') - if (this.isRecordingActions) { - this.recordAction( - { - actionId: 'set_crosspoint', - options: { level: [level], source: source, dest: dest }, - }, - `connect dest ${dest} level ${level}` - ) - } + this.record_crosspoint(source, dest, level) } export function SetCrosspoint(sourceN, destN, levelN) { diff --git a/src/feedbacks.js b/src/feedbacks.js index afd7ce3..79566da 100644 --- a/src/feedbacks.js +++ b/src/feedbacks.js @@ -12,7 +12,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.purple, }, options: [{ ...feedbackOptions.levels, choices: self.levels }], - callback: async (feedback) => { + callback: (feedback) => { let l = feedback.options.level.length let k = self.selected_level.length @@ -41,7 +41,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.purple, }, options: [{ ...feedbackOptions.levels, choices: self.levels }, feedbackOptions.destination], - callback: async (feedback) => { + callback: (feedback) => { if (self.selected_dest === feedback.options.dest) { let l = feedback.options.level.length let k = self.selected_level.length @@ -74,7 +74,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.green, }, options: [feedbackOptions.destination], - callback: async (feedback) => { + callback: (feedback) => { if (self.selected_dest === feedback.options.dest) { return true } else { @@ -92,7 +92,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.cyan, }, options: [feedbackOptions.source], - callback: async (feedback) => { + callback: (feedback) => { if (self.selected_source === feedback.options.source) { return true } else { @@ -110,7 +110,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.orange, }, options: [feedbackOptions.source], - callback: async (feedback) => { + callback: (feedback) => { // look for this dest in route table console.log('dest:source feedback ' + self.selected_dest + ':' + feedback.options.source) for (let i = 0; i < self.routeTable.length; i++) { @@ -133,7 +133,7 @@ export async function UpdateFeedbacks(self) { bgcolor: colours.orange, }, options: [feedbackOptions.source, feedbackOptions.destination], - callback: async (feedback) => { + callback: (feedback) => { // look for this dest in route table console.log('dest:source feedback ' + feedback.options.dest + ':' + feedback.options.source) for (let i = 0; i < self.routeTable.length; i++) { @@ -145,6 +145,9 @@ export async function UpdateFeedbacks(self) { } return false }, + subscribe: (feedback) => { + self.getCrosspoints(feedback.options.dest) + } } feedbackDefinitions['crosspoint_connected_by_name'] = { type: 'boolean', @@ -185,6 +188,17 @@ export async function UpdateFeedbacks(self) { } return false }, + subscribe: async (feedback, context) => { + const dest = parseInt(await context.parseVariablesInString(feedback.options.dest)) + if (isNaN(dest) || dest < 1 || dest > 65536) { + self.log( + 'warn', + `crosspoint_connected_by_name:Subscribe has been passed an out of range variable - dst ${dest}` + ) + return undefined + } + self.getCrosspoints(dest) + }, } self.setFeedbackDefinitions(feedbackDefinitions) diff --git a/src/index.js b/src/index.js index c3fa78a..c8c9686 100644 --- a/src/index.js +++ b/src/index.js @@ -41,18 +41,10 @@ class SW_P_08 extends InstanceBase { } async init(config) { - this.updateStatus(InstanceStatus.Connecting) - this.config = config - this.setupVariables() - this.updateFeedbacks() - this.updateActions() - this.updatePresets() - this.init_tcp() - this.checkFeedbacks('selected_level', 'selected_level_dest', 'selected_dest', 'selected_source') + this.configUpdated(config) } async configUpdated(config) { - this.log('debug', 'update config') this.updateStatus(InstanceStatus.Connecting) this.config = config this.setupVariables() @@ -60,10 +52,16 @@ class SW_P_08 extends InstanceBase { this.updateActions() this.updatePresets() this.init_tcp() - this.checkFeedbacks('selected_level', 'selected_level_dest', 'selected_dest', 'selected_source') + this.checkFeedbacks( + 'selected_level', + 'selected_level_dest', + 'selected_dest', + 'selected_source', + 'crosspoint_connected', + 'crosspoint_connected_by_name' + ) } - // When module gets deleted async destroy() { this.log('debug', `destroy. ID: ${this.id}`) this.stopKeepAliveTimer() @@ -73,7 +71,6 @@ class SW_P_08 extends InstanceBase { this.updateStatus(InstanceStatus.Disconnected) } - // Track whether actions are being recorded handleStartStopRecordActions(isRecording) { this.isRecordingActions = isRecording } diff --git a/src/tcp.js b/src/tcp.js index cf9f408..6ebf225 100644 --- a/src/tcp.js +++ b/src/tcp.js @@ -85,11 +85,14 @@ export function init_tcp() { this.socket.on('connect', () => { this.updateStatus(InstanceStatus.Ok, 'Connected') - this.startKeepAliveTimer() if (this.config.supported_commands_on_connect === true) { // request protocol implementation this.sendMessage('61019E') } + this.subscribeActions() + this.subscribeFeedbacks() + this.startKeepAliveTimer() + this.checkFeedbacks() }) this.socket.on('data', (chunk) => { From c61afed3b02e323db719594c006cb24d6369ea41 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Tue, 9 Jul 2024 15:43:25 +1000 Subject: [PATCH 23/25] update_crosspoints argument saftey --- src/crosspoints.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/crosspoints.js b/src/crosspoints.js index 455eb4f..a395e89 100644 --- a/src/crosspoints.js +++ b/src/crosspoints.js @@ -83,17 +83,17 @@ export function SetCrosspoint(sourceN, destN, levelN) { this.log('debug', 'Crosspoint ' + sourceN + '>' + destN + ' level ' + levelN) console.log('SetCrosspoint ' + sourceN + '>' + destN + ' level ' + levelN) - if (sourceN <= 0 || sourceN > 65536) { + if (isNaN(sourceN) || sourceN <= 0 || sourceN > 65536) { this.log('warn', 'Unable to route source ' + sourceN) return } - if (destN <= 0 || destN > 65536) { + if (isNaN(destN) || destN <= 0 || destN > 65536) { this.log('warn', 'Unable to route destination ' + destN) return } - if (levelN <= 0 || levelN > 256) { + if (isNaN(levelN) || levelN <= 0 || levelN > 256) { this.log('warn', 'Unable to route level ' + levelN) return } From a92d70d4578c2acc0a62a3ccf9d2d303a75afc96 Mon Sep 17 00:00:00 2001 From: Phillip Ivan Pietruschka Date: Thu, 11 Jul 2024 10:40:52 +1000 Subject: [PATCH 24/25] help.md - fix typos --- companion/HELP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/companion/HELP.md b/companion/HELP.md index 424fa51..dd7e23e 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -33,8 +33,8 @@ There are multiple ways of making crosspoint buttons to cater for different appl ## Feedbacks Button background colours can be changed to show current selection status. -- **Crosspoiint Connected** -- **Crosspoiint Connected By Name** +- **Crosspoint Connected** +- **Crosspoint Connected By Name** - **Selected Levels** - **Selected Levels and Destination** - **Selected Destination** @@ -54,7 +54,7 @@ Some dynamic information is stored in variables which you can access through the ## Action Recorder -While recording, tally and connected messages recieved from the router will create new set crosspoint actions. Allowing for easy creation of salvos. +Tally and connected messages recieved from the router will create new set crosspoint actions, allowing for easy creation of salvos. ## Version History From b7333b77c480d289ab1012d250b9b689bd9a77f2 Mon Sep 17 00:00:00 2001 From: phillipivan Date: Mon, 22 Jul 2024 09:56:19 +1000 Subject: [PATCH 25/25] package.json - specify type:module. manifest.json - update maintainers --- companion/manifest.json | 4 ++++ package.json | 1 + 2 files changed, 5 insertions(+) diff --git a/companion/manifest.json b/companion/manifest.json index 54cb96a..cbcc004 100644 --- a/companion/manifest.json +++ b/companion/manifest.json @@ -10,6 +10,10 @@ "maintainers": [ { "name": "Peter Daniel" + }, + { + "name": "Phillip Ivan Pietruschka", + "email": "ivanpietruschka@gmail.com" } ], "runtime": { diff --git a/package.json b/package.json index 6c67772..1ff774e 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "generic-swp08", "version": "2.0.0", "main": "src/index.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "format": "prettier --write",