code for proxy (for testing)

This commit is contained in:
micuat 2022-02-28 11:03:44 +01:00
parent 1a3af2f782
commit 34d0579018
12 changed files with 6619 additions and 933 deletions

View file

@ -1,12 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = LF
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = LF
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

358
.eslintrc
View file

@ -1,179 +1,179 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"globals": {
"document": false,
"escape": false,
"navigator": false,
"unescape": false,
"window": false,
"describe": true,
"before": true,
"it": true,
"expect": true,
"sinon": true
},
"parser": "babel-eslint",
"plugins": [
],
"rules": {
"block-scoped-var": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": [2, { "properties": "always" }],
"comma-dangle": [2, "always-multiline"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,
"consistent-return": 2,
"consistent-this": 0,
"curly": [2, "multi-line"],
"default-case": 0,
"dot-location": [2, "property"],
"dot-notation": 0,
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"func-names": 0,
"func-style": 0,
"generator-star-spacing": [2, "both"],
"guard-for-in": 0,
"handle-callback-err": [0, "^(err|error|anySpecificError)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": [2, {"before": true, "after": true}],
"linebreak-style": 0,
"max-depth": 0,
"max-len": [2, 120, 4],
"max-nested-callbacks": 0,
"max-params": 0,
"max-statements": 0,
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"newline-after-var": [2, "always"],
"new-parens": 2,
"no-alert": 0,
"no-array-constructor": 2,
"no-bitwise": 0,
"no-caller": 2,
"no-catch-shadow": 0,
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 0,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-else-return": 2,
"no-empty": 0,
"no-empty-character-class": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 0,
"no-extra-strict": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inline-comments": 0,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 0,
"no-lonely-if": 0,
"no-loop-func": 0,
"no-mixed-requires": 0,
"no-mixed-spaces-and-tabs": [2, false],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 0,
"no-new": 2,
"no-new-func": 2,
"no-new-object": 2,
"no-new-require": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-path-concat": 0,
"no-plusplus": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-reserved-keys": 0,
"no-restricted-modules": 0,
"no-return-assign": 2,
"no-script-url": 0,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-underscore-dangle": 0,
"no-unneeded-ternary": 2,
"no-unreachable": 1,
"no-unused-expressions": 0,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
"no-use-before-define": 2,
"no-var": 2,
"no-void": 0,
"no-warning-comments": 0,
"no-with": 2,
"one-var": 0,
"operator-assignment": 0,
"operator-linebreak": [2, "after"],
"padded-blocks": 0,
"prefer-const": 2,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": 0,
"sort-vars": 0,
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
"space-in-brackets": 0,
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always"],
"strict": 2,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": [2, "any"],
"wrap-regex": 0,
"yoda": [2, "never"]
}
}
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"globals": {
"document": false,
"escape": false,
"navigator": false,
"unescape": false,
"window": false,
"describe": true,
"before": true,
"it": true,
"expect": true,
"sinon": true
},
"parser": "babel-eslint",
"plugins": [
],
"rules": {
"block-scoped-var": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"camelcase": [2, { "properties": "always" }],
"comma-dangle": [2, "always-multiline"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"complexity": 0,
"consistent-return": 2,
"consistent-this": 0,
"curly": [2, "multi-line"],
"default-case": 0,
"dot-location": [2, "property"],
"dot-notation": 0,
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"func-names": 0,
"func-style": 0,
"generator-star-spacing": [2, "both"],
"guard-for-in": 0,
"handle-callback-err": [0, "^(err|error|anySpecificError)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": [2, {"before": true, "after": true}],
"linebreak-style": 0,
"max-depth": 0,
"max-len": [2, 120, 4],
"max-nested-callbacks": 0,
"max-params": 0,
"max-statements": 0,
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"newline-after-var": [2, "always"],
"new-parens": 2,
"no-alert": 0,
"no-array-constructor": 2,
"no-bitwise": 0,
"no-caller": 2,
"no-catch-shadow": 0,
"no-cond-assign": 2,
"no-console": 0,
"no-constant-condition": 0,
"no-continue": 0,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-div-regex": 0,
"no-dupe-args": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-else-return": 2,
"no-empty": 0,
"no-empty-character-class": 2,
"no-eq-null": 0,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": 0,
"no-extra-semi": 0,
"no-extra-strict": 0,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inline-comments": 0,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 0,
"no-lonely-if": 0,
"no-loop-func": 0,
"no-mixed-requires": 0,
"no-mixed-spaces-and-tabs": [2, false],
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-nested-ternary": 0,
"no-new": 2,
"no-new-func": 2,
"no-new-object": 2,
"no-new-require": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-path-concat": 0,
"no-plusplus": 0,
"no-process-env": 0,
"no-process-exit": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-reserved-keys": 0,
"no-restricted-modules": 0,
"no-return-assign": 2,
"no-script-url": 0,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-sync": 0,
"no-ternary": 0,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-underscore-dangle": 0,
"no-unneeded-ternary": 2,
"no-unreachable": 1,
"no-unused-expressions": 0,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
"no-use-before-define": 2,
"no-var": 2,
"no-void": 0,
"no-warning-comments": 0,
"no-with": 2,
"one-var": 0,
"operator-assignment": 0,
"operator-linebreak": [2, "after"],
"padded-blocks": 0,
"prefer-const": 2,
"quote-props": 0,
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": 0,
"sort-vars": 0,
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
"space-in-brackets": 0,
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [2, "always"],
"strict": 2,
"use-isnan": 2,
"valid-jsdoc": 0,
"valid-typeof": 2,
"vars-on-top": 0,
"wrap-iife": [2, "any"],
"wrap-regex": 0,
"yoda": [2, "never"]
}
}

46
.gitignore vendored
View file

@ -1,23 +1,23 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
.DS_Store
node_modules
.project
.com.greenworldsoft.syncfolderspro
# Remove some common IDE working directories
.idea
.vscode
.DS_Store
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
.DS_Store
node_modules
.project
.com.greenworldsoft.syncfolderspro
# Remove some common IDE working directories
.idea
.vscode
.DS_Store

42
LICENSE
View file

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

View file

@ -1,27 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.dmx-web.server</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/dmx-web</string>
<string>-c</string>
<string>/Library/Server/DMX/dmx-web.json</string>
</array>
<key>StandardOutPath</key>
<string>/Library/Logs/homebridge/dmxlogfile.log</string>
<key>StandardErrorPath</key>
<string>/Library/Logs/homebridge/dmxlogfile.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin/:$PATH</string>
</dict>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.dmx-web.server</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/dmx-web</string>
<string>-c</string>
<string>/Library/Server/DMX/dmx-web.json</string>
</array>
<key>StandardOutPath</key>
<string>/Library/Logs/homebridge/dmxlogfile.log</string>
<key>StandardErrorPath</key>
<string>/Library/Logs/homebridge/dmxlogfile.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin/:$PATH</string>
</dict>
</dict>
</plist>

View file

@ -1,197 +1,197 @@
{
"_title": "DMX Dashboard",
"_comment1": "To have a different page title change _title to title.",
"server": {
"listen_port": 8080,
"listen_host": "::",
"_uid": "www-data",
"_gid": "www-data",
"_comment1": "To drop privileges change the '_uid' and '_gid' keys to 'uid' and 'gid'.",
"_comment2": "On macOS uid and gid must be set in the launchd script."
},
"presets": [
{
"label": "White",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4": 255, "5": 255, "6": 255, "16": 16, "17": 255, "18": 0, "19": 255, "20": 255, "21": 255 }
}
},
{
"label": "Natural",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4":255, "5": 190, "6": 140, "16": 16, "17": 255, "18": 0, "19": 255, "20": 190, "21":140 }
}
},
{
"label": "Worklight",
"values": {
"office": { "1": 16, "2": 130, "3": 0, "4": 255, "5": 165, "6": 0, "16": 1, "17": 255, "18": 0, "19": 255, "20": 190, "21": 140, "22": 0, "23": 0, "24": 0, "25": 255, "26": 190, "27": 140 }
}
},
{
"label": "Chill",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4": 255, "5": 39, "6": 0, "16": 1, "17": 255, "18": 0, "19": 255, "20": 255, "21": 0, "22": 0, "23": 0, "24": 0, "25": 128, "26": 0, "27": 255, "32": 255, "33": 60 }
}
},
{
"label": "Cinema",
"values": {
"office": { "1": 16, "2": 30, "3":0, "4": 255, "5": 39, "6": 0, "16": 0, "32": 255, "33": 0 }
}
}
],
"universes": {
"office": {
"output": {
"driver": "enttec-usb-dmx-pro",
"device": "/dev/cu.usbserial-6AVNHXS8"
},
"devices": [
{
"type": "eurolite-led-bar",
"address": 1,
"name": "Behind the TV",
"_comment1": "name is optional and is shown above the sliders if sets"
},
{
"type": "eurolite-led-bar",
"address": 16
},
{
"type": "showtec-multidim2",
"address": 32
},
{
"type": "stairville-led-par-56",
"address": 64
},
{
"type": "stairville-led-par-56",
"address": 70
},
{
"type": "stairville-led-par-56",
"address": 76
},
{
"type": "oppsk-cob-uv-par",
"address": 100
},
{
"type": "lixda-par12-led",
"address": 110
},
{
"type": "lixda-par12-led",
"address": 120
},
{
"type": "lixda-par12-led",
"address": 130
},
{
"type": "lixda-par12-led",
"address": 140
}
]
},
"bedroom": {
"output": {
"driver": "dmxking-ultra-dmx-pro",
"device": "/dev/cu.usbserial-6AVNHXS9",
"options": {
"port": "A"
}
},
"devices": [
{
"type": "eurolite-led-bar",
"address": 1
},
{
"type": "eurolite-led-bar",
"address": 16
},
{
"type": "showtec-multidim2",
"address": 32
},
{
"type": "stairville-led-par-56",
"address": 64
},
{
"type": "stairville-led-par-56",
"address": 70
},
{
"type": "stairville-led-par-56",
"address": 76
}
]
},
"livingroom": {
"output": {
"driver": "dmxking-ultra-dmx-pro",
"device": "/dev/cu.usbserial-6AVNHXS4",
"options": {
"port": "B"
}
},
"devices": [
{
"type": "eurolite-led-bar",
"address": 1
},
{
"type": "eurolite-led-bar",
"address": 16
},
{
"type": "showtec-multidim2",
"address": 32
},
{
"type": "stairville-led-par-56",
"address": 64
},
{
"type": "stairville-led-par-56",
"address": 70
},
{
"type": "stairville-led-par-56",
"address": 76
}
]
}
},
"_animPresets": [{
"_comment1": "Change _animPresets to animPresets for a new tab with all preset animations",
"_comment2": "Check documentation for how to make animations",
"label": "test",
"anim": {
"office": [{
"to": {
"10": 0,
"20": 0
}
},
{
"to": {
"10": 255
},
"duration": 2000
},
{
"to": {
"20": 255
},
"duration": 1000
}
]
}
}]
}
{
"_title": "DMX Dashboard",
"_comment1": "To have a different page title change _title to title.",
"server": {
"listen_port": 8080,
"listen_host": "::",
"_uid": "www-data",
"_gid": "www-data",
"_comment1": "To drop privileges change the '_uid' and '_gid' keys to 'uid' and 'gid'.",
"_comment2": "On macOS uid and gid must be set in the launchd script."
},
"presets": [
{
"label": "White",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4": 255, "5": 255, "6": 255, "16": 16, "17": 255, "18": 0, "19": 255, "20": 255, "21": 255 }
}
},
{
"label": "Natural",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4":255, "5": 190, "6": 140, "16": 16, "17": 255, "18": 0, "19": 255, "20": 190, "21":140 }
}
},
{
"label": "Worklight",
"values": {
"office": { "1": 16, "2": 130, "3": 0, "4": 255, "5": 165, "6": 0, "16": 1, "17": 255, "18": 0, "19": 255, "20": 190, "21": 140, "22": 0, "23": 0, "24": 0, "25": 255, "26": 190, "27": 140 }
}
},
{
"label": "Chill",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4": 255, "5": 39, "6": 0, "16": 1, "17": 255, "18": 0, "19": 255, "20": 255, "21": 0, "22": 0, "23": 0, "24": 0, "25": 128, "26": 0, "27": 255, "32": 255, "33": 60 }
}
},
{
"label": "Cinema",
"values": {
"office": { "1": 16, "2": 30, "3":0, "4": 255, "5": 39, "6": 0, "16": 0, "32": 255, "33": 0 }
}
}
],
"universes": {
"office": {
"output": {
"driver": "enttec-usb-dmx-pro",
"device": "/dev/cu.usbserial-6AVNHXS8"
},
"devices": [
{
"type": "eurolite-led-bar",
"address": 1,
"name": "Behind the TV",
"_comment1": "name is optional and is shown above the sliders if sets"
},
{
"type": "eurolite-led-bar",
"address": 16
},
{
"type": "showtec-multidim2",
"address": 32
},
{
"type": "stairville-led-par-56",
"address": 64
},
{
"type": "stairville-led-par-56",
"address": 70
},
{
"type": "stairville-led-par-56",
"address": 76
},
{
"type": "oppsk-cob-uv-par",
"address": 100
},
{
"type": "lixda-par12-led",
"address": 110
},
{
"type": "lixda-par12-led",
"address": 120
},
{
"type": "lixda-par12-led",
"address": 130
},
{
"type": "lixda-par12-led",
"address": 140
}
]
},
"bedroom": {
"output": {
"driver": "dmxking-ultra-dmx-pro",
"device": "/dev/cu.usbserial-6AVNHXS9",
"options": {
"port": "A"
}
},
"devices": [
{
"type": "eurolite-led-bar",
"address": 1
},
{
"type": "eurolite-led-bar",
"address": 16
},
{
"type": "showtec-multidim2",
"address": 32
},
{
"type": "stairville-led-par-56",
"address": 64
},
{
"type": "stairville-led-par-56",
"address": 70
},
{
"type": "stairville-led-par-56",
"address": 76
}
]
},
"livingroom": {
"output": {
"driver": "dmxking-ultra-dmx-pro",
"device": "/dev/cu.usbserial-6AVNHXS4",
"options": {
"port": "B"
}
},
"devices": [
{
"type": "eurolite-led-bar",
"address": 1
},
{
"type": "eurolite-led-bar",
"address": 16
},
{
"type": "showtec-multidim2",
"address": 32
},
{
"type": "stairville-led-par-56",
"address": 64
},
{
"type": "stairville-led-par-56",
"address": 70
},
{
"type": "stairville-led-par-56",
"address": 76
}
]
}
},
"_animPresets": [{
"_comment1": "Change _animPresets to animPresets for a new tab with all preset animations",
"_comment2": "Check documentation for how to make animations",
"label": "test",
"anim": {
"office": [{
"to": {
"10": 0,
"20": 0
}
},
{
"to": {
"10": 255
},
"duration": 2000
},
{
"to": {
"20": 255
},
"duration": 1000
}
]
}
}]
}

View file

@ -1,128 +1,149 @@
#!/usr/bin/env node
const fs = require('fs');
const http = require('http');
const body = require('body-parser');
const express = require('express');
const socketio = require('socket.io');
const program = require('commander');
const DMX = require('dmx');
const A = DMX.Animation;
program
.version('0.0.1')
.option('-c, --config <file>', 'Read config from file [/etc/dmx-web.json]', '/etc/dmx-web.json')
.parse(process.argv);
const config = JSON.parse(fs.readFileSync(program.config, 'utf8'));
function DMXWeb() {
const app = express();
const server = http.createServer(app);
const io = socketio.listen(server);
const dmx = new DMX(config);
for (const universe in config.universes) {
dmx.addUniverse(
universe,
config.universes[universe].output.driver,
config.universes[universe].output.device,
config.universes[universe].output.options
);
}
const listenPort = config.server.listen_port || 8080;
const listenHost = config.server.listen_host || '::';
server.listen(listenPort, listenHost, null, () => {
if (config.server.uid && config.server.gid) {
try {
process.setuid(config.server.uid);
process.setgid(config.server.gid);
} catch (err) {
console.log(err);
process.exit(1);
}
}
});
app.use(body.json());
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.get('/config', (req, res) => {
const response = {'devices': dmx.devices, 'universes': {}};
Object.keys(config.universes).forEach(key => {
response.universes[key] = config.universes[key].devices;
});
res.json(response);
});
app.get('/state/:universe', (req, res) => {
if (!(req.params.universe in dmx.universes)) {
res.status(404).json({'error': 'universe not found'});
return;
}
res.json({'state': dmx.universeToObject(req.params.universe)});
});
app.post('/state/:universe', (req, res) => {
if (!(req.params.universe in dmx.universes)) {
res.status(404).json({'error': 'universe not found'});
return;
}
dmx.update(req.params.universe, req.body);
res.json({'state': dmx.universeToObject(req.params.universe)});
});
app.post('/animation/:universe', (req, res) => {
try {
const universe = dmx.universes[req.params.universe];
// preserve old states
const old = dmx.universeToObject(req.params.universe);
const animation = new A();
for (const step in req.body) {
animation.add(
req.body[step].to,
req.body[step].duration || 0,
req.body[step].options || {}
);
}
animation.add(old, 0);
animation.run(universe);
res.json({'success': true});
} catch (e) {
console.log(e);
res.json({'error': String(e)});
}
});
io.sockets.on('connection', socket => {
socket.emit('init', {'devices': dmx.devices, 'setup': config});
socket.on('request_refresh', () => {
for (const universe in config.universes) {
socket.emit('update', universe, dmx.universeToObject(universe));
}
});
socket.on('update', (universe, update) => {
dmx.update(universe, update);
});
dmx.on('update', (universe, update) => {
socket.emit('update', universe, update);
});
});
}
DMXWeb();
#!/usr/bin/env node
const fs = require('fs');
const http = require('http');
const body = require('body-parser');
const express = require('express');
const socketio = require('socket.io');
const socketioClient = require('socket.io-client');
const program = require('commander');
const DMX = require('dmx');
const A = DMX.Animation;
program
.version('0.0.1')
.option('-c, --config <file>', 'Read config from file [/etc/dmx-web.json]', '/etc/dmx-web.json')
.parse(process.argv);
const config = JSON.parse(fs.readFileSync(program.config, 'utf8'));
function DMXWeb() {
const app = express();
const server = http.createServer(app);
const io = socketio.listen(server);
const dmx = new DMX(config);
for (const universe in config.universes) {
dmx.addUniverse(
universe,
config.universes[universe].output.driver,
config.universes[universe].output.device,
config.universes[universe].output.options
);
}
const listenPort = config.server.listen_port || 8080;
const listenHost = config.server.listen_host || '::';
server.listen(listenPort, listenHost, null, () => {
if (config.server.uid && config.server.gid) {
try {
process.setuid(config.server.uid);
process.setgid(config.server.gid);
} catch (err) {
console.log(err);
process.exit(1);
}
}
});
app.use(body.json());
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.get('/config', (req, res) => {
const response = {'devices': dmx.devices, 'universes': {}};
Object.keys(config.universes).forEach(key => {
response.universes[key] = config.universes[key].devices;
});
res.json(response);
});
app.get('/state/:universe', (req, res) => {
if (!(req.params.universe in dmx.universes)) {
res.status(404).json({'error': 'universe not found'});
return;
}
res.json({'state': dmx.universeToObject(req.params.universe)});
});
app.post('/state/:universe', (req, res) => {
if (!(req.params.universe in dmx.universes)) {
res.status(404).json({'error': 'universe not found'});
return;
}
dmx.update(req.params.universe, req.body);
res.json({'state': dmx.universeToObject(req.params.universe)});
});
app.post('/animation/:universe', (req, res) => {
try {
const universe = dmx.universes[req.params.universe];
// preserve old states
const old = dmx.universeToObject(req.params.universe);
const animation = new A();
for (const step in req.body) {
animation.add(
req.body[step].to,
req.body[step].duration || 0,
req.body[step].options || {}
);
}
animation.add(old, 0);
animation.run(universe);
res.json({'success': true});
} catch (e) {
console.log(e);
res.json({'error': String(e)});
}
});
const socket = socketioClient("https://light.glitches.me");
socket.on("register", () => {
socket.emit('init', {'devices': dmx.devices, 'setup': config});
})
socket.on('request_refresh', () => {
for (const universe in config.universes) {
socket.emit('update', universe, dmx.universeToObject(universe));
}
});
socket.on('update', (universe, update) => {
dmx.update(universe, update);
});
dmx.on('update', (universe, update) => {
socket.emit('update', universe, update);
});
io.sockets.on('connection', socket => {
console.log("new client")
socket.emit('init', {'devices': dmx.devices, 'setup': config});
socket.on('request_refresh', () => {
for (const universe in config.universes) {
socket.emit('update', universe, dmx.universeToObject(universe));
}
});
socket.on('update', (universe, update) => {
dmx.update(universe, update);
});
dmx.on('update', (universe, update) => {
socket.emit('update', universe, update);
});
});
}
DMXWeb();

121
dmx-web.json Normal file
View file

@ -0,0 +1,121 @@
{
"_title": "DMX Dashboard",
"_comment1": "To have a different page title change _title to title.",
"server": {
"listen_port": 8080,
"listen_host": "::",
"_uid": "www-data",
"_gid": "www-data",
"_comment1": "To drop privileges change the '_uid' and '_gid' keys to 'uid' and 'gid'.",
"_comment2": "On macOS uid and gid must be set in the launchd script."
},
"presets": [
{
"label": "White",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4": 255, "5": 255, "6": 255, "16": 16, "17": 255, "18": 0, "19": 255, "20": 255, "21": 255 }
}
},
{
"label": "Natural",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4":255, "5": 190, "6": 140, "16": 16, "17": 255, "18": 0, "19": 255, "20": 190, "21":140 }
}
},
{
"label": "Worklight",
"values": {
"office": { "1": 16, "2": 130, "3": 0, "4": 255, "5": 165, "6": 0, "16": 1, "17": 255, "18": 0, "19": 255, "20": 190, "21": 140, "22": 0, "23": 0, "24": 0, "25": 255, "26": 190, "27": 140 }
}
},
{
"label": "Chill",
"values": {
"office": { "1": 16, "2": 255, "3": 0, "4": 255, "5": 39, "6": 0, "16": 1, "17": 255, "18": 0, "19": 255, "20": 255, "21": 0, "22": 0, "23": 0, "24": 0, "25": 128, "26": 0, "27": 255, "32": 255, "33": 60 }
}
},
{
"label": "Cinema",
"values": {
"office": { "1": 16, "2": 30, "3":0, "4": 255, "5": 39, "6": 0, "16": 0, "32": 255, "33": 0 }
}
}
],
"universes": {
"office": {
"output": {
"driver": "enttec-usb-dmx-pro",
"device": "/dev/ttyUSB0"
},
"devices": [
{
"type": "generic-rgb",
"address": 4,
"name": "rgb"
},
{
"type": "generic-rgb",
"address": 8,
"name": "rgb"
},
{
"type": "generic-rgb",
"address": 12,
"name": "rgb"
},
{
"type": "generic-rgb",
"address": 16,
"name": "rgb"
},
{
"type": "generic-rgb",
"address": 20,
"name": "rgb"
},
{
"type": "generic-rgb",
"address": 24,
"name": "rgb"
},
{
"type": "generic-rgb",
"address": 28,
"name": "rgb"
},
{
"type": "generic-rgb",
"address": 32,
"name": "rgb"
}
]
}
},
"_animPresets": [{
"_comment1": "Change _animPresets to animPresets for a new tab with all preset animations",
"_comment2": "Check documentation for how to make animations",
"label": "test",
"anim": {
"office": [{
"to": {
"10": 0,
"20": 0
}
},
{
"to": {
"10": 255
},
"duration": 2000
},
{
"to": {
"20": 255
},
"duration": 1000
}
]
}
}]
}

View file

@ -1,256 +1,256 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>DMX Lichtschalter</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes" />
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
<style>
body {
background: #222;
color: #eee;
}
h1 {
clear: both;
}
input[type=range] {
-webkit-appearance: slider-vertical;
height: 100px;
width: 60px;
margin-bottom: 2em;
margin-top: 3em;
}
input[type=range]:before {
content: attr(value);
color: #eee;
font-family: monospace;
text-align: center;
width: 60px;
display: block;
color: #fff;
position: absolute;
top: 2em;
}
.device {
clear: both;
padding-top: 1em;
}
.channel {
position: relative;
text-align: center;
float: left;
}
.btn {
margin-bottom: 1em;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
function get_html_id(universe, channel) {
return 'channel_' + universe + '_' + channel;
}
function rgb_to_array(str) {
matches = str.match(/^#([0-9a-f]{6})$/i)
if (!matches) {
return [0,0,0]
}
m = matches[1];
if(m) {
return [
parseInt(m.substr(0,2),16),
parseInt(m.substr(2,2),16),
parseInt(m.substr(4,2),16)
];
}
}
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding;
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
}
function array_to_rgb(arr) {
return '#'+decimalToHex(arr[0])+decimalToHex(arr[1])+decimalToHex(arr[2]);
}
var socket = io();
socket.on('init', function (msg) {
$('#presets').empty();
$('#sliders').empty();
$('#anim').empty();
setup = msg.setup
devices = msg.devices
if(setup.title !== undefined) {
document.title = setup.title;
}
/* preset buttons */
for(var preset in setup.presets) {
var html = '<button class="span2 btn btn-info">' + setup.presets[preset].label + '</button>';
var e = $(html)
e.hide().appendTo('#presets').fadeIn();
e.click(function(values) { return function() {
for(var universe in values) {
socket.emit('update', universe, values[universe]);
}
};}(setup.presets[preset].values));
}
/* blackout button */
var blackout = $('<button class="span2 btn btn-danger">Black Out</button>');
blackout.hide().appendTo('#presets').fadeIn();
blackout.click(function() {
for(var universe in setup.universes) {
var u = {};
for(var i = 0; i < 255; i++) {
u[i] = 0;
}
socket.emit('update', universe, u);
}
});
/* sliders */
for(var universe in setup.universes) {
var html = "<div><h1>" + universe + "</h1>";
for(var device in setup.universes[universe].devices) {
var dev = setup.universes[universe].devices[device];
html += '<div class="device">';
if(dev.name !== undefined) {
html += '<h4 class="name">'+dev.name+'</h4>'
}
for(var channel in devices[dev.type].channels) {
var channel_id = dev.address + Number(channel)
var html_id = get_html_id(universe, channel_id);
html += '<div class="channel">'
html += '<label for="' + html_id + '">' + devices[dev.type].channels[channel] + '</label>';
html += '<input id="' + html_id + '" type="range" min="0" value="0" max="255" orient="vertical">'
html += '</div>'
}
if(devices[dev.type].channels.includes('red') && devices[dev.type].channels.includes('blue') && devices[dev.type].channels.includes('green')) {
var html_id = get_html_id(universe, dev.address);
html += '<input id="picker_'+html_id+'" type="color" data-red="'+devices[dev.type].channels.indexOf('red');
html += '" data-green="'+devices[dev.type].channels.indexOf('green')+'" data-blue="'+devices[dev.type].channels.indexOf('blue')+'"/>';
}
html += '</div>'
}
html += "</div>";
$(html).hide().appendTo('#sliders').fadeIn();
}
/* animations */
for(var animation in setup.animPresets) {
var html = '<button class="span2 btn btn-info">' + setup.animPresets[animation].label + '</button>';
var e = $(html)
e.hide().appendTo('#anim').fadeIn();
e.click(function(values) { return function() {
for(var universe in values) {
$.ajax({
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(values[universe]),
url: '/animation/'+universe,
processData: false,
dataType: 'json'
});
}
};}(setup.animPresets[animation].anim));
}
$("input").live("change", function(e) {
var i = e.target.id.split('_');
if(i[0] === 'picker') {
var u = {};
var tar = $(e.target);
var value = rgb_to_array(tar.val());
u[Number(i[3])+Number(tar.data('red'))] = value[0];
u[Number(i[3])+Number(tar.data('green'))] = value[1];
u[Number(i[3])+Number(tar.data('blue'))] = value[2];
socket.emit('update', i[2], u);
}
else {
var u = {};
u[i[2]] = e.target.value;
socket.emit('update', i[1], u);
}
});
socket.emit('request_refresh');
});
socket.on('update', function (universe, update) {
for(var k in update) {
$('#' + get_html_id(universe, k)).attr('value', update[k]);
}
$("[id^=picker_channel_"+universe+"]").each(function(index, value){
var self = $(this);
var id = self.attr('id').split('_');
var obj = self.data();
var arr = Object.keys(obj).map(function(key){return obj[key];});
var _id = Number(id[3]);
var min = _id;
var max = min;
min += Math.min.apply( null, arr );
max += Math.max.apply( null, arr );
for(var k in update) {
if(k >= min && k <= max) {
var values = rgb_to_array(self.val());
var address = k - _id;
if(address === Number(self.data('red'))) {
values[0] = update[k];
}
else if(address === Number(self.data('green'))) {
values[1] = update[k];
}
else if(address === Number(self.data('blue'))) {
values[2] = update[k];
}
self.val(array_to_rgb(values));
}
}
});
});
</script>
</head>
<body>
<div class="navbar navbar-inverse">
<div class="navbar-inner">
<ul class="nav" id="myTab">
<li class="active"><a href="#home" data-toggle="tab">Home</a></li>
<li><a href="#sliders" data-toggle="tab">Sliders</a></li>
<li><a href="#anim" data-toggle="tab">Animations</a></li>
<!--<li><a href="#scripts" data-toggle="tab">Scripts</a></li>-->
</ul>
</div>
</div>
<div class="container-fluid">
<div class="tab-content">
<div id="home" class="tab-pane active">
<div class="row-fluid" id="presets">
</div>
</div>
<div id="sliders" class="tab-pane">
</div>
<div id="anim" class="tab-pane">
</div>
<div id="scripts" class="tab-pane">
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>DMX Lichtschalter</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="apple-mobile-web-app-capable" content="yes" />
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
<style>
body {
background: #222;
color: #eee;
}
h1 {
clear: both;
}
input[type=range] {
-webkit-appearance: slider-vertical;
height: 100px;
width: 60px;
margin-bottom: 2em;
margin-top: 3em;
}
input[type=range]:before {
content: attr(value);
color: #eee;
font-family: monospace;
text-align: center;
width: 60px;
display: block;
color: #fff;
position: absolute;
top: 2em;
}
.device {
clear: both;
padding-top: 1em;
}
.channel {
position: relative;
text-align: center;
float: left;
}
.btn {
margin-bottom: 1em;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
function get_html_id(universe, channel) {
return 'channel_' + universe + '_' + channel;
}
function rgb_to_array(str) {
matches = str.match(/^#([0-9a-f]{6})$/i)
if (!matches) {
return [0,0,0]
}
m = matches[1];
if(m) {
return [
parseInt(m.substr(0,2),16),
parseInt(m.substr(2,2),16),
parseInt(m.substr(4,2),16)
];
}
}
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding;
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
}
function array_to_rgb(arr) {
return '#'+decimalToHex(arr[0])+decimalToHex(arr[1])+decimalToHex(arr[2]);
}
var socket = io();
socket.on('init', function (msg) {
$('#presets').empty();
$('#sliders').empty();
$('#anim').empty();
setup = msg.setup
devices = msg.devices
if(setup.title !== undefined) {
document.title = setup.title;
}
/* preset buttons */
for(var preset in setup.presets) {
var html = '<button class="span2 btn btn-info">' + setup.presets[preset].label + '</button>';
var e = $(html)
e.hide().appendTo('#presets').fadeIn();
e.click(function(values) { return function() {
for(var universe in values) {
socket.emit('update', universe, values[universe]);
}
};}(setup.presets[preset].values));
}
/* blackout button */
var blackout = $('<button class="span2 btn btn-danger">Black Out</button>');
blackout.hide().appendTo('#presets').fadeIn();
blackout.click(function() {
for(var universe in setup.universes) {
var u = {};
for(var i = 0; i < 255; i++) {
u[i] = 0;
}
socket.emit('update', universe, u);
}
});
/* sliders */
for(var universe in setup.universes) {
var html = "<div><h1>" + universe + "</h1>";
for(var device in setup.universes[universe].devices) {
var dev = setup.universes[universe].devices[device];
html += '<div class="device">';
if(dev.name !== undefined) {
html += '<h4 class="name">'+dev.name+'</h4>'
}
for(var channel in devices[dev.type].channels) {
var channel_id = dev.address + Number(channel)
var html_id = get_html_id(universe, channel_id);
html += '<div class="channel">'
html += '<label for="' + html_id + '">' + devices[dev.type].channels[channel] + '</label>';
html += '<input id="' + html_id + '" type="range" min="0" value="0" max="255" orient="vertical">'
html += '</div>'
}
if(devices[dev.type].channels.includes('red') && devices[dev.type].channels.includes('blue') && devices[dev.type].channels.includes('green')) {
var html_id = get_html_id(universe, dev.address);
html += '<input id="picker_'+html_id+'" type="color" data-red="'+devices[dev.type].channels.indexOf('red');
html += '" data-green="'+devices[dev.type].channels.indexOf('green')+'" data-blue="'+devices[dev.type].channels.indexOf('blue')+'"/>';
}
html += '</div>'
}
html += "</div>";
$(html).hide().appendTo('#sliders').fadeIn();
}
/* animations */
for(var animation in setup.animPresets) {
var html = '<button class="span2 btn btn-info">' + setup.animPresets[animation].label + '</button>';
var e = $(html)
e.hide().appendTo('#anim').fadeIn();
e.click(function(values) { return function() {
for(var universe in values) {
$.ajax({
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(values[universe]),
url: '/animation/'+universe,
processData: false,
dataType: 'json'
});
}
};}(setup.animPresets[animation].anim));
}
$("input").live("change", function(e) {
var i = e.target.id.split('_');
if(i[0] === 'picker') {
var u = {};
var tar = $(e.target);
var value = rgb_to_array(tar.val());
u[Number(i[3])+Number(tar.data('red'))] = value[0];
u[Number(i[3])+Number(tar.data('green'))] = value[1];
u[Number(i[3])+Number(tar.data('blue'))] = value[2];
socket.emit('update', i[2], u);
}
else {
var u = {};
u[i[2]] = e.target.value;
socket.emit('update', i[1], u);
}
});
socket.emit('request_refresh');
});
socket.on('update', function (universe, update) {
for(var k in update) {
$('#' + get_html_id(universe, k)).attr('value', update[k]);
}
$("[id^=picker_channel_"+universe+"]").each(function(index, value){
var self = $(this);
var id = self.attr('id').split('_');
var obj = self.data();
var arr = Object.keys(obj).map(function(key){return obj[key];});
var _id = Number(id[3]);
var min = _id;
var max = min;
min += Math.min.apply( null, arr );
max += Math.max.apply( null, arr );
for(var k in update) {
if(k >= min && k <= max) {
var values = rgb_to_array(self.val());
var address = k - _id;
if(address === Number(self.data('red'))) {
values[0] = update[k];
}
else if(address === Number(self.data('green'))) {
values[1] = update[k];
}
else if(address === Number(self.data('blue'))) {
values[2] = update[k];
}
self.val(array_to_rgb(values));
}
}
});
});
</script>
</head>
<body>
<div class="navbar navbar-inverse">
<div class="navbar-inner">
<ul class="nav" id="myTab">
<li class="active"><a href="#home" data-toggle="tab">Home</a></li>
<li><a href="#sliders" data-toggle="tab">Sliders</a></li>
<li><a href="#anim" data-toggle="tab">Animations</a></li>
<!--<li><a href="#scripts" data-toggle="tab">Scripts</a></li>-->
</ul>
</div>
</div>
<div class="container-fluid">
<div class="tab-content">
<div id="home" class="tab-pane active">
<div class="row-fluid" id="presets">
</div>
</div>
<div id="sliders" class="tab-pane">
</div>
<div id="anim" class="tab-pane">
</div>
<div id="scripts" class="tab-pane">
</div>
</div>
</div>
</body>
</html>

5543
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,35 +1,36 @@
{
"name": "dmx-web",
"version": "1.0.1",
"author": "Sebastian Wiedenroth <wiedi@frubar.net>",
"description": "DMX webservice and web ui powered by node-dmx",
"url": "https://github.com/node-dmx/dmx-web",
"bin": {
"dmx-web": "./dmx-web.js"
},
"scripts": {
"test": "node_modules/eslint/bin/eslint.js -c .eslintrc .; exit 0"
},
"repository": {
"type": "git",
"url": "https://github.com/node-dmx/dmx-web.git"
},
"keywords": [
"DMX",
"light control"
],
"dependencies": {
"dmx": "^0.2.0",
"commander": "^2.12.2",
"express": "^4.16.2",
"socket.io": "^2.0.4"
},
"devDependencies": {
"babel-eslint": "^10.0.1",
"eslint": "^5.0.1"
},
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
}
{
"name": "dmx-web",
"version": "1.0.1",
"author": "Sebastian Wiedenroth <wiedi@frubar.net>",
"description": "DMX webservice and web ui powered by node-dmx",
"url": "https://github.com/node-dmx/dmx-web",
"bin": {
"dmx-web": "./dmx-web.js"
},
"scripts": {
"test": "node_modules/eslint/bin/eslint.js -c .eslintrc .; exit 0"
},
"repository": {
"type": "git",
"url": "https://github.com/node-dmx/dmx-web.git"
},
"keywords": [
"DMX",
"light control"
],
"dependencies": {
"commander": "^2.12.2",
"dmx": "^0.2.0",
"express": "^4.16.2",
"socket.io": "^2.0.4",
"socket.io-client": "^4.4.1"
},
"devDependencies": {
"babel-eslint": "^10.0.1",
"eslint": "^5.0.1"
},
"license": "MIT",
"engines": {
"node": ">=10.0.0"
}
}

110
readme.md
View file

@ -1,55 +1,55 @@
# node-dmx
Webinterface and HTTP API using [node-dmx](https://github.com/node-dmx/dmx)
## Install
`npm install -g dmx-web`
## Webinterface
### Configuration
The Daemon `dmx-web` looks for a configuration file in `/etc/dmx-web.json`. An alternate location can be passed as a command line argument.
This configuration file consists of three sections:
- Server
- Universes
- Presets
In the Server section you can set the listen port and host.
Under Universes you describe the DMX Universes with details like which output driver to use and which devices are at which address.
The presets section allows you to specify a state some channels should be set when the preset is called.
A example configuration is in the repository by the name `dmx-web-example.conf`
### Run
`dmx-web [-c <full-path to config file>]`
### Run as a service
On MacOS you can run dmx-web as a service by adding a launch script to `/Library/LaunchDaemons`. See the example file.
### Animation HTTP API
A List of Channel Transistions can be POSTed to `/animation/<universe>`. Each transistion is a JSON Object with at least the `to` property present. The Value of which also has to be an Object describing the channel end-states.
A duration for this transistion can be given in the `duration` property.
If not specified 0ms is assumed.
Example:
[
{"to": {"10": 0, "20": 0}},
{"to": {"10": 255}, "duration": 2000},
{"to": {"20": 255}, "duration": 1000}
]
This sets channels 10 and 20 to zero. Then transistions channel 10 to 255 in 2 seconds. After that channel 20 is faded to 255 in 1 second.
## Community
We're happy to help. Chat with us on IRC in #dmx on libera.chat.
# node-dmx
Webinterface and HTTP API using [node-dmx](https://github.com/node-dmx/dmx)
## Install
`npm install -g dmx-web`
## Webinterface
### Configuration
The Daemon `dmx-web` looks for a configuration file in `/etc/dmx-web.json`. An alternate location can be passed as a command line argument.
This configuration file consists of three sections:
- Server
- Universes
- Presets
In the Server section you can set the listen port and host.
Under Universes you describe the DMX Universes with details like which output driver to use and which devices are at which address.
The presets section allows you to specify a state some channels should be set when the preset is called.
A example configuration is in the repository by the name `dmx-web-example.conf`
### Run
`dmx-web [-c <full-path to config file>]`
### Run as a service
On MacOS you can run dmx-web as a service by adding a launch script to `/Library/LaunchDaemons`. See the example file.
### Animation HTTP API
A List of Channel Transistions can be POSTed to `/animation/<universe>`. Each transistion is a JSON Object with at least the `to` property present. The Value of which also has to be an Object describing the channel end-states.
A duration for this transistion can be given in the `duration` property.
If not specified 0ms is assumed.
Example:
[
{"to": {"10": 0, "20": 0}},
{"to": {"10": 255}, "duration": 2000},
{"to": {"20": 255}, "duration": 1000}
]
This sets channels 10 and 20 to zero. Then transistions channel 10 to 255 in 2 seconds. After that channel 20 is faded to 255 in 1 second.
## Community
We're happy to help. Chat with us on IRC in #dmx on libera.chat.