From 36ff2404bd3f48ed582c9ec1358a0e5b0d749164 Mon Sep 17 00:00:00 2001 From: dstoll Date: Sat, 17 Feb 2024 15:05:24 -0800 Subject: [PATCH 1/3] Added Ember+ support for sending boolean data to Ember elements for Lawo AfV and other broadcast tally --- package-lock.json | 299 +++++++++++++++++++++++++++++++++++++++++++ package.json | 3 +- src/actions/Ember.ts | 119 +++++++++++++++++ 3 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 src/actions/Ember.ts diff --git a/package-lock.json b/package-lock.json index 8f4cb9b3..cd86aa78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "jquery": "^3.6.0", "jsonwebtoken": "^9.0.0", "jspack": "^0.0.4", + "node-emberplus": "^3.0.5", "obs-websocket-js": "npm:obs-websocket-js@^4.0.3", "obs-websocket-js-5": "npm:obs-websocket-js@^5.0.2", "osc": "^2.4.3", @@ -874,6 +875,14 @@ "node": ">= 10" } }, + "node_modules/@types/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@types/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", @@ -985,6 +994,11 @@ "@types/node": "*" } }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -1031,6 +1045,14 @@ "@types/node": "*" } }, + "node_modules/@types/safer-buffer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/safer-buffer/-/safer-buffer-2.1.0.tgz", + "integrity": "sha512-04WlrCdOLy1Ejpwc3A7qyZzsH6uqeWoH+XO80V8S8NRubGg+E4FMMM3VAS6jZZ8w+dXki1/5FI5upmMDQlaQsQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.8.tgz", @@ -1923,6 +1945,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "engines": { + "node": ">=0.1.90" + } + }, "node_modules/colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", @@ -2143,6 +2173,14 @@ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, + "node_modules/cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2826,6 +2864,14 @@ ], "optional": true }, + "node_modules/eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==", + "engines": { + "node": "> 0.1.90" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3051,6 +3097,15 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "node_modules/gdnet-asn1": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gdnet-asn1/-/gdnet-asn1-1.0.0.tgz", + "integrity": "sha512-oIFd2YLpXwlBQCl7ZOAtl1ZkZVjnoKhsLJrOPeeBkPUxlUQ2pUmQiGKCLMMNjFYRK+fvERl6O4MZnghVBYRXBg==", + "dependencies": { + "@types/safer-buffer": "^2.1.0", + "safer-buffer": "~2.1.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -3475,6 +3530,11 @@ "ws": "*" } }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "node_modules/jake": { "version": "10.8.7", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", @@ -3834,6 +3894,68 @@ "dev": true, "optional": true }, + "node_modules/node-emberplus": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/node-emberplus/-/node-emberplus-3.0.5.tgz", + "integrity": "sha512-PVnPNKpYoJ72ga8QZ40Cv3h5mmbDuZ7JbHpWu6x8FvVQ4H3cu4NLeqx1l3b8bxCVz8NmbE+3yuoRvC03wQhMfw==", + "dependencies": { + "@types/asn1": "^0.2.0", + "@types/long": "^4.0.0", + "@types/safer-buffer": "2.1.0", + "gdnet-asn1": "1.0.0", + "long": "^4.0.0", + "reflect-metadata": "0.1.13", + "smart-buffer": "^3.0.3", + "typedi": "0.8.0", + "winston": "^2.1.1", + "winston-color": "^1.0.0", + "yargs": "^17.5.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/node-emberplus/node_modules/@types/node": { + "version": "7.10.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.14.tgz", + "integrity": "sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA==" + }, + "node_modules/node-emberplus/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/node-emberplus/node_modules/smart-buffer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-3.0.3.tgz", + "integrity": "sha512-s5+gEstGAW315m+SbWCVjhf0+YZxqaeP6DieqzoHTHKghjyn0PhNhmR2qsqNNBaj8bBrwjmuxhXVKrGZqGphXQ==", + "dependencies": { + "@types/node": "^7.0.4" + }, + "engines": { + "node": ">= 4.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/node-emberplus/node_modules/winston": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", + "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", + "dependencies": { + "async": "^2.6.4", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/node-gyp-build": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", @@ -5204,6 +5326,11 @@ "node": ">= 0.6" } }, + "node_modules/typedi": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/typedi/-/typedi-0.8.0.tgz", + "integrity": "sha512-/c7Bxnm6eh5kXx2I+mTuO+2OvoWni5+rXA3PhXwVWCtJRYmz3hMok5s1AKLzoDvNAZqj/Q/acGstN0ri5aQoOA==" + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -5379,6 +5506,38 @@ "node": ">= 12.0.0" } }, + "node_modules/winston-color": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/winston-color/-/winston-color-1.0.0.tgz", + "integrity": "sha512-I6bB13vkglrYfOna1YoSFt6eHeafP6r2w+G192vyfgtKBnxoxXCV+TbiY3IJTT+kW3Stqsb6TJzT+ZGHXsRxDw==", + "dependencies": { + "winston": "^2.1.1" + } + }, + "node_modules/winston-color/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/winston-color/node_modules/winston": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", + "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", + "dependencies": { + "async": "^2.6.4", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/winston-transport": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", @@ -6262,6 +6421,14 @@ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true }, + "@types/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@types/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-V91DSJ2l0h0gRhVP4oBfBzRBN9lAbPUkGDMCnwedqPKX2d84aAMc9CulOvxdw1f7DfEYx99afab+Rsm3e52jhA==", + "requires": { + "@types/node": "*" + } + }, "@types/body-parser": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", @@ -6373,6 +6540,11 @@ "@types/node": "*" } }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", @@ -6419,6 +6591,14 @@ "@types/node": "*" } }, + "@types/safer-buffer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/safer-buffer/-/safer-buffer-2.1.0.tgz", + "integrity": "sha512-04WlrCdOLy1Ejpwc3A7qyZzsH6uqeWoH+XO80V8S8NRubGg+E4FMMM3VAS6jZZ8w+dXki1/5FI5upmMDQlaQsQ==", + "requires": { + "@types/node": "*" + } + }, "@types/semver": { "version": "7.3.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.8.tgz", @@ -7136,6 +7316,11 @@ "simple-swizzle": "^0.2.2" } }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==" + }, "colorspace": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", @@ -7308,6 +7493,11 @@ "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, + "cycle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", + "integrity": "sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7824,6 +8014,11 @@ "dev": true, "optional": true }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==" + }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8004,6 +8199,15 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "gdnet-asn1": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gdnet-asn1/-/gdnet-asn1-1.0.0.tgz", + "integrity": "sha512-oIFd2YLpXwlBQCl7ZOAtl1ZkZVjnoKhsLJrOPeeBkPUxlUQ2pUmQiGKCLMMNjFYRK+fvERl6O4MZnghVBYRXBg==", + "requires": { + "@types/safer-buffer": "^2.1.0", + "safer-buffer": "~2.1.0" + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -8306,6 +8510,11 @@ "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", "requires": {} }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + }, "jake": { "version": "10.8.7", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", @@ -8592,6 +8801,60 @@ "dev": true, "optional": true }, + "node-emberplus": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/node-emberplus/-/node-emberplus-3.0.5.tgz", + "integrity": "sha512-PVnPNKpYoJ72ga8QZ40Cv3h5mmbDuZ7JbHpWu6x8FvVQ4H3cu4NLeqx1l3b8bxCVz8NmbE+3yuoRvC03wQhMfw==", + "requires": { + "@types/asn1": "^0.2.0", + "@types/long": "^4.0.0", + "@types/safer-buffer": "2.1.0", + "gdnet-asn1": "1.0.0", + "long": "^4.0.0", + "reflect-metadata": "0.1.13", + "smart-buffer": "^3.0.3", + "typedi": "0.8.0", + "winston": "^2.1.1", + "winston-color": "^1.0.0", + "yargs": "^17.5.1" + }, + "dependencies": { + "@types/node": { + "version": "7.10.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.14.tgz", + "integrity": "sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA==" + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, + "smart-buffer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-3.0.3.tgz", + "integrity": "sha512-s5+gEstGAW315m+SbWCVjhf0+YZxqaeP6DieqzoHTHKghjyn0PhNhmR2qsqNNBaj8bBrwjmuxhXVKrGZqGphXQ==", + "requires": { + "@types/node": "^7.0.4" + } + }, + "winston": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", + "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", + "requires": { + "async": "^2.6.4", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + } + } + } + }, "node-gyp-build": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", @@ -9615,6 +9878,11 @@ "mime-types": "~2.1.24" } }, + "typedi": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/typedi/-/typedi-0.8.0.tgz", + "integrity": "sha512-/c7Bxnm6eh5kXx2I+mTuO+2OvoWni5+rXA3PhXwVWCtJRYmz3hMok5s1AKLzoDvNAZqj/Q/acGstN0ri5aQoOA==" + }, "typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -9758,6 +10026,37 @@ } } }, + "winston-color": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/winston-color/-/winston-color-1.0.0.tgz", + "integrity": "sha512-I6bB13vkglrYfOna1YoSFt6eHeafP6r2w+G192vyfgtKBnxoxXCV+TbiY3IJTT+kW3Stqsb6TJzT+ZGHXsRxDw==", + "requires": { + "winston": "^2.1.1" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "requires": { + "lodash": "^4.17.14" + } + }, + "winston": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.7.tgz", + "integrity": "sha512-vLB4BqzCKDnnZH9PHGoS2ycawueX4HLqENXQitvFHczhgW2vFpSOn31LZtVr1KU8YTw7DS4tM+cqyovxo8taVg==", + "requires": { + "async": "^2.6.4", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + } + } + } + }, "winston-transport": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", diff --git a/package.json b/package.json index 80d47ffe..a052ade8 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "Matteo Gheza ", "chanx", "Sam Yoffe", - "David Stevens", + "David Stevens", "David Prows", "monkzz", "Greg Oseid" @@ -74,6 +74,7 @@ "jquery": "^3.6.0", "jsonwebtoken": "^9.0.0", "jspack": "^0.0.4", + "node-emberplus": "^3.0.5", "obs-websocket-js": "npm:obs-websocket-js@^4.0.3", "obs-websocket-js-5": "npm:obs-websocket-js@^5.0.2", "osc": "^2.4.3", diff --git a/src/actions/Ember.ts b/src/actions/Ember.ts new file mode 100644 index 00000000..73bd60b8 --- /dev/null +++ b/src/actions/Ember.ts @@ -0,0 +1,119 @@ +import { EmberClient } from "node-emberplus/lib/client/ember-client"; +import { logger } from ".."; +import { RegisterAction } from "../_decorators/RegisterAction"; +import { Action } from "./_Action"; +import { EmberClientEvent } from "node-emberplus"; + + +@RegisterAction("48c73ee4", "Ember+", [ + { fieldName: "ip", fieldLabel: "IP Address", fieldType: "text" }, + { fieldName: "port", fieldLabel: "Port", fieldType: "port" }, + { fieldName: "path", fieldLabel: "Ember Path", fieldType: "text" }, + { fieldName: "value", fieldLabel: "Value", fieldType: "bool" }, +]) +export class Ember extends Action { + // to prevent the lag of connecting to Ember+, we will store a dictionary of all Ember+ connections + // keyed on the IP:port. and a similar connection status in an identical dictionary. + + // A string or number "path" for the Ember+ parameter to be set must be found using Ember+ Viewer + // or a similar tool. This has been tested on a Lawo 6.4.0.19 audio cosole and known to be working + // vGPIs are found in _3/_70/ + // that path corresponds to the descriptions "Signals/GVR" + // In Lawo 6.4, the ember tree is broken up by HLSD numbers + // In Lawo 10.x, virtual GPI/O are defined all together in the tree and can be found by name more easily. + // NOTE: The parameter value being set by this module is designed to be of a boolean type or used + // like a boolean. An int64 type Ember+ element is fine, so long as the use is 0/1 logic similar to C + // code. MCX 6.4.x uses an int64 value for virtul GPI/O. MCX 10.x uses an actual boolean type. + // this code is compatible with both. However, if you intend to set a string value, the results could be + // unpredictable. This code might need to be refactored if string support is required in the future. + private static emberConnections = {}; + private static emberConnectionStatus = {}; + + private getConnection() { + const connection = `${this.action.data.ip}:${this.action.data.port}`; + return Ember.emberConnections[connection]; + } + + private isConnected() { + const connection = `${this.action.data.ip}:${this.action.data.port}`; + return Ember.emberConnectionStatus[connection]; + } + + private newConnection() { + logger(`Ember+ Creating new connection for ${this.action.data.ip}:${this.action.data.port}`); + const connection = `${this.action.data.ip}:${this.action.data.port}`; + Ember.emberConnections[connection] = new EmberClient({ + host: this.action.data.ip, + port: this.action.data.port + }) + .on(EmberClientEvent.ERROR, (e) => {logger(`Ember+ protocol error: ${e}`, "error");}) + .on(EmberClientEvent.DISCONNECTED, async (e) => { + Ember.emberConnectionStatus[connection] = false; + logger(`Ember+ client disconnected - reconnecting: erorr: ${e}`); + await this.getConnection().connectAsync(); + }) + .on(EmberClientEvent.CONNECTED, () => { + logger(`Ember+ Connected.`); + Ember.emberConnectionStatus[connection] = true; + + }) + .on(EmberClientEvent.CONNECTING, () => { + logger(`Ember+ Connecting.`); + Ember.emberConnectionStatus[connection] = false; + }); + } + + private connectionExists() { + const connection = `${this.action.data.ip}:${this.action.data.port}`; + return connection in Ember.emberConnections; + } + + private async connect() { + logger(`Ember+ Connecting to ${this.action.data.ip}:${this.action.data.port}`); + try { + await this.getConnection().connectAsync(); + } catch (error) { + logger(`Ember+ connection error: ${error}`, "error"); + } + } + + private async setGPI() { + if(!this.isConnected) { + logger(`Ember+ error: not connected. Ignoring request.`, "error"); + return; + } + + logger(`Ember+ sending message ${this.action.data.value} to ${this.action.data.ip}:${this.action.data.port} on path ${this.action.data.path}`); + try { + var node = await this.getConnection().getElementByPathAsync(this.action.data.path); + + await this.getConnection().setValueAsync( + node, + this.action.data.value + ); + } catch (error) { + logger(`Ember+ error, giving up sending: ${error}`, "error"); + } + } + + public run(): void { + // first check to see if this client exists, if not create it + if (!this.connectionExists()) { + logger(`Ember+ Creating new connection.`); + try { + this.newConnection(); + this.connect(); + + } catch (error) { + logger(`Ember+ Caught error trying to create new connection: ${error}`, "error"); + } + } + + // send message + try { + this.setGPI(); + } catch (error) { + logger(`Ember+ An error occured sending Ember+: ${error}`, "error"); + } + } +} From fc3df72f20384ba4679bcbd8fbef97c540624be8 Mon Sep 17 00:00:00 2001 From: Dave Stoll Date: Sat, 17 Feb 2024 16:12:09 -0800 Subject: [PATCH 2/3] Fixed issue with action value not being present if the boolean was false --- src/actions/Ember.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/actions/Ember.ts b/src/actions/Ember.ts index 73bd60b8..3b842d43 100644 --- a/src/actions/Ember.ts +++ b/src/actions/Ember.ts @@ -86,10 +86,19 @@ export class Ember extends Action { logger(`Ember+ sending message ${this.action.data.value} to ${this.action.data.ip}:${this.action.data.port} on path ${this.action.data.path}`); try { var node = await this.getConnection().getElementByPathAsync(this.action.data.path); + + // since a boolean value results in a checkbox in the HTML admin page, we don't always get a value:false in the json. + // if the value is undefined we will assume it should be "false" + var emberValue = false; + if (this.action.data.value === undefined) { + emberValue = false; + } else { + emberValue = this.action.data.value; + } await this.getConnection().setValueAsync( node, - this.action.data.value + emberValue ); } catch (error) { logger(`Ember+ error, giving up sending: ${error}`, "error"); From 038b77d28798dd059d12747cc6db72d904715b7a Mon Sep 17 00:00:00 2001 From: dstoll Date: Sun, 18 Feb 2024 01:10:42 -0800 Subject: [PATCH 3/3] Added Ember+ documentation information --- docs/docs/usage/sections/devices.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/docs/usage/sections/devices.md b/docs/docs/usage/sections/devices.md index d4f3c693..8c34783d 100644 --- a/docs/docs/usage/sections/devices.md +++ b/docs/docs/usage/sections/devices.md @@ -28,5 +28,8 @@ The following Device Actions are implemented: * Generic TCP/UDP * Local Console Output/Logging (useful for testing) * Open Sound Control (OSC) (multiple arguments supported) +* Ember+ support for setting virtual GPI (boolean or int64 as boolean) Device Actions can only be run once when the device state enters or exits that bus. This is to prevent actions from being run continuously if tally data is received in chunks. To run an action again, a device must change state on that specific bus (Preview or Program) before it can be run again. + +Ember+ device IP, port and Ember tree path must be specified in the action. Device path may be retrieved using Ember+ Viewer (freely available under BSL-1.0 license). More information on Ember+ may be found at github.com/Lawo/ember-plus. Ember+ vGPIO tally tested on Lawo MCX 6.4 and 10.8. \ No newline at end of file