├── .gitignore ├── app └── plugins │ └── system_controller │ └── gpio-buttons │ ├── i18n │ └── strings_en.json │ ├── uninstall.sh │ ├── install.sh │ ├── package.json │ ├── config.json │ ├── index.js │ └── UIConfig.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /app/plugins/system_controller/gpio-buttons/i18n/strings_en.json: -------------------------------------------------------------------------------- 1 | { 2 | "pin":"Pin", 3 | "action":"Action" 4 | } 5 | -------------------------------------------------------------------------------- /app/plugins/system_controller/gpio-buttons/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Removing gpio-buttons" 4 | rm -rf /data/configuration/miscellanea/gpio-buttons 5 | echo "Done" 6 | 7 | echo "pluginuninstallend" -------------------------------------------------------------------------------- /app/plugins/system_controller/gpio-buttons/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Initializing config" 4 | 5 | # compatibilty with earlier config files due to few commands name change and directory rename 6 | if [ -f /data/configuration/miscellanea/gpio-buttons/config.json ];then 7 | mv /data/configuration/miscellanea/gpio-buttons /data/configuration/system_controller/gpio-buttons 8 | sed -i 's/playpause/playPause/g' /data/configuration/system_controller/gpio-buttons/config.json 9 | sed -i 's/volup/volumeUp/g' /data/configuration/system_controller/gpio-buttons/config.json 10 | sed -i 's/voldown/volumeDown/g' /data/configuration/system_controller/gpio-buttons/config.json 11 | fi 12 | 13 | echo "plugininstallend" 14 | -------------------------------------------------------------------------------- /app/plugins/system_controller/gpio-buttons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gpio-buttons", 3 | "version": "0.8.0", 4 | "description": "GPIO button-listener plugin for Volumio2", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "tomatpasser", 10 | "license": "ISC", 11 | "volumio_info": { 12 | "prettyName": "GPIO Buttons", 13 | "icon": "fa-microchip", 14 | "plugin_type": "system_controller", 15 | "boot_priority": 10 16 | }, 17 | "dependencies": { 18 | "fs-extra": "^0.28.0", 19 | "kew": "^0.7.0", 20 | "v-conf": "^0.10.0", 21 | "onoff": "^1.1.1", 22 | "socket.io-client": "^1.4.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/plugins/system_controller/gpio-buttons/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "playPause":{ 3 | "enabled": {"value": false, "type": "boolean"}, 4 | "pin": {"value": 17,"type":"number"}, 5 | "value": {"value": 0, "type": "number"} 6 | }, 7 | "volumeUp":{ 8 | "enabled": {"value": false, "type": "boolean"}, 9 | "pin": {"value": 18,"type":"number"}, 10 | "value": {"value": 0, "type": "number"} 11 | }, 12 | "volumeDown":{ 13 | "enabled": {"value": false, "type": "boolean"}, 14 | "pin": {"value": 22,"type":"number"}, 15 | "value": {"value": 0, "type": "number"} 16 | }, 17 | "previous":{ 18 | "enabled": {"value": false, "type": "boolean"}, 19 | "pin": {"value": 23,"type":"number"}, 20 | "value": {"value": 0, "type": "number"} 21 | }, 22 | "next":{ 23 | "enabled": {"value": false, "type": "boolean"}, 24 | "pin": {"value": 24,"type":"number"}, 25 | "value": {"value": 0, "type": "number"} 26 | }, 27 | "shutdown":{ 28 | "enabled": {"value": false, "type": "boolean"}, 29 | "pin": {"value": 3,"type":"number"}, 30 | "value": {"value": 0, "type": "number"} 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gpio-buttons 2 | 3 | NOTE: This repository is not up to date, development has moved here: https://github.com/volumio/volumio-plugins/tree/master/plugins/system_controller/gpio-buttons 4 | 5 | Work in progress for a GPIO button plugin for volumio2 on the raspberry pi. Uses node js library 'onoff'. 6 | 7 | Currently 6 actions can be attached to gpio pins: Play/pause, Vol+, Vol-, Next track, Previous track and shutdown. 8 | 9 | ![GPIO Buttons interface](http://tomatpasser.dk/gpio-buttons2.png) 10 | 11 | #Installation 12 | 13 | - Download zip for install in volumio2 here: http://tomatpasser.dk/gpio-buttons.zip 14 | - Got to Plugins -> Upload plugin and upload the file 15 | - Each action can then be enabled and the GPIO pin selected. 16 | 17 | The pin numbers entered should be GPIO pin numbers. The GPIO pins have an internal pull-up/pull-down resistor. The default value for each pin is shown below. As of now it is not possible to control the pull in the configuration. 18 | 19 | __Remember never to connect 5V to the GPIO pins, only 3.3V or ground.__ 20 | 21 | __Connection of a GPIO to a voltage higher than 3.3V will likely destroy the GPIO block within the SoC.__ 22 | 23 | The button should be wired between the GPIO pin and __opposite__ voltage as the default pull. The plugin will detect any change on the GPIO pin. 24 | 25 | Example: If using GPIO pin 17, the button should be wired between the pin and 3.3V (high) because the default pull is low for GPIO pin 17. 26 | 27 | 28 | | GPIO Pin | Default pull | GPIO Pin | Default pull | 29 | | :-----------: |:-------------:| :-----------: |:-------------:| 30 | | 2 | high | 15 | low | 31 | | 3 | high | 16 | low | 32 | | 4 | high | 17 | low | 33 | | 5 | high | 18 | low | 34 | | 6 | high | 19 | low | 35 | | 7 | high | 20 | low | 36 | | 8 | high | 21 | low | 37 | | 9 | low | 22 | low | 38 | | 10 | low | 23 | low | 39 | | 11 | low | 24 | low | 40 | | 12 | low | 25 | low | 41 | | 13 | low | 26 | low | 42 | | 14 | low | 27 | low | 43 | 44 | __Schematic showing wiring example for pin 17__ 45 | 46 | 47 | 48 | #TODO 49 | - Control of internal pull resistor 50 | - Support for rotary encoder to control volume 51 | - Custom commands 52 | -------------------------------------------------------------------------------- /app/plugins/system_controller/gpio-buttons/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var libQ = require('kew'); 4 | var fs = require('fs-extra'); 5 | var Gpio = require('onoff').Gpio; 6 | var io = require('socket.io-client'); 7 | var socket = io.connect('http://localhost:3000'); 8 | var actions = ["playPause", "volumeUp", "volumeDown", "previous", "next", "shutdown"]; 9 | 10 | module.exports = GPIOButtons; 11 | 12 | function GPIOButtons(context) { 13 | var self = this; 14 | self.context=context; 15 | self.commandRouter = self.context.coreCommand; 16 | self.logger = self.context.logger; 17 | self.triggers = []; 18 | } 19 | 20 | 21 | GPIOButtons.prototype.onVolumioStart = function () { 22 | var self = this; 23 | 24 | var configFile=this.commandRouter.pluginManager.getConfigurationFile(this.context,'config.json'); 25 | this.config = new (require('v-conf'))(); 26 | this.config.loadFile(configFile); 27 | 28 | self.logger.info("GPIO-Buttons initialized"); 29 | 30 | return libQ.resolve(); 31 | }; 32 | 33 | 34 | GPIOButtons.prototype.getConfigurationFiles = function() 35 | { 36 | return ['config.json']; 37 | }; 38 | 39 | 40 | GPIOButtons.prototype.onStart = function () { 41 | var self = this; 42 | var defer=libQ.defer(); 43 | 44 | self.createTriggers() 45 | .then (function (result) { 46 | self.logger.info("GPIO-Buttons started"); 47 | defer.resolve(); 48 | }); 49 | 50 | return defer.promise; 51 | }; 52 | 53 | 54 | GPIOButtons.prototype.onStop = function () { 55 | var self = this; 56 | var defer=libQ.defer(); 57 | 58 | self.clearTriggers() 59 | .then (function (result) { 60 | self.logger.info("GPIO-Buttons stopped"); 61 | defer.resolve(); 62 | }); 63 | 64 | return defer.promise; 65 | }; 66 | 67 | 68 | GPIOButtons.prototype.onRestart = function () { 69 | var self = this; 70 | }; 71 | 72 | GPIOButtons.prototype.onInstall = function () { 73 | var self = this; 74 | }; 75 | 76 | GPIOButtons.prototype.onUninstall = function () { 77 | var self = this; 78 | }; 79 | 80 | GPIOButtons.prototype.getConf = function (varName) { 81 | var self = this; 82 | }; 83 | 84 | GPIOButtons.prototype.setConf = function(varName, varValue) { 85 | var self = this; 86 | }; 87 | 88 | GPIOButtons.prototype.getAdditionalConf = function (type, controller, data) { 89 | var self = this; 90 | }; 91 | 92 | GPIOButtons.prototype.setAdditionalConf = function () { 93 | var self = this; 94 | }; 95 | 96 | GPIOButtons.prototype.setUIConfig = function (data) { 97 | var self = this; 98 | }; 99 | 100 | 101 | GPIOButtons.prototype.getUIConfig = function () { 102 | var defer = libQ.defer(); 103 | var self = this; 104 | 105 | self.logger.info('GPIO-Buttons: Getting UI config'); 106 | 107 | //Just for now.. 108 | var lang_code = 'en'; 109 | 110 | //var lang_code = this.commandRouter.sharedVars.get('language_code'); 111 | 112 | self.commandRouter.i18nJson(__dirname+'/i18n/strings_'+lang_code+'.json', 113 | __dirname+'/i18n/strings_en.json', 114 | __dirname + '/UIConfig.json') 115 | .then(function(uiconf) 116 | { 117 | 118 | var i = 0; 119 | actions.forEach(function(action, index, array) { 120 | 121 | // Strings for config 122 | var c1 = action.concat('.enabled'); 123 | var c2 = action.concat('.pin'); 124 | 125 | // accessor supposes actions and uiconfig items are in SAME order 126 | // this is potentially dangerous: rewrite with a JSON search of "id" value ? 127 | uiconf.sections[0].content[2*i].value = self.config.get(c1); 128 | uiconf.sections[0].content[2*i+1].value.value = self.config.get(c2); 129 | uiconf.sections[0].content[2*i+1].value.label = self.config.get(c2).toString(); 130 | 131 | i = i + 1; 132 | }); 133 | 134 | defer.resolve(uiconf); 135 | }) 136 | .fail(function() 137 | { 138 | defer.reject(new Error()); 139 | }); 140 | 141 | return defer.promise; 142 | }; 143 | 144 | 145 | GPIOButtons.prototype.saveConfig = function(data) 146 | { 147 | var self = this; 148 | 149 | actions.forEach(function(action, index, array) { 150 | // Strings for data fields 151 | var s1 = action.concat('Enabled'); 152 | var s2 = action.concat('Pin'); 153 | 154 | // Strings for config 155 | var c1 = action.concat('.enabled'); 156 | var c2 = action.concat('.pin'); 157 | var c3 = action.concat('.value'); 158 | 159 | self.config.set(c1, data[s1]); 160 | self.config.set(c2, data[s2]['value']); 161 | self.config.set(c3, 0); 162 | }); 163 | 164 | self.clearTriggers() 165 | .then(self.createTriggers()); 166 | 167 | self.commandRouter.pushToastMessage('success',"GPIO-Buttons", "Configuration saved"); 168 | }; 169 | 170 | 171 | GPIOButtons.prototype.createTriggers = function() { 172 | var self = this; 173 | 174 | self.logger.info('GPIO-Buttons: Reading config and creating triggers...'); 175 | 176 | actions.forEach(function(action, index, array) { 177 | var c1 = action.concat('.enabled'); 178 | var c2 = action.concat('.pin'); 179 | 180 | var enabled = self.config.get(c1); 181 | var pin = self.config.get(c2); 182 | 183 | if(enabled === true){ 184 | self.logger.info('GPIO-Buttons: '+ action + ' on pin ' + pin); 185 | var j = new Gpio(pin,'in','both'); 186 | j.watch(self.listener.bind(self,action)); 187 | self.triggers.push(j); 188 | } 189 | }); 190 | 191 | return libQ.resolve(); 192 | }; 193 | 194 | 195 | GPIOButtons.prototype.clearTriggers = function () { 196 | var self = this; 197 | 198 | self.triggers.forEach(function(trigger, index, array) { 199 | self.logger.info("GPIO-Buttons: Destroying trigger " + index); 200 | 201 | trigger.unwatchAll(); 202 | trigger.unexport(); 203 | }); 204 | 205 | self.triggers = []; 206 | 207 | return libQ.resolve(); 208 | }; 209 | 210 | 211 | GPIOButtons.prototype.listener = function(action,err,value){ 212 | var self = this; 213 | 214 | var c3 = action.concat('.value'); 215 | var lastvalue = self.config.get(c3); 216 | 217 | // IF change AND high (or low?) 218 | if(value !== lastvalue && value === 1){ 219 | //do thing 220 | self[action](); 221 | } 222 | // remember value 223 | self.config.set(c3,value); 224 | }; 225 | 226 | 227 | 228 | 229 | 230 | //Play / Pause 231 | GPIOButtons.prototype.playPause = function() { 232 | //this.logger.info('GPIO-Buttons: Play/pause button pressed'); 233 | socket.emit('getState',''); 234 | socket.once('pushState', function (state) { 235 | if(state.status=='play' && state.service=='webradio'){ 236 | socket.emit('stop'); 237 | } else if(state.status=='play'){ 238 | socket.emit('pause'); 239 | } else { 240 | socket.emit('play'); 241 | } 242 | }); 243 | }; 244 | 245 | //next on playlist 246 | GPIOButtons.prototype.next = function() { 247 | //this.logger.info('GPIO-Buttons: next-button pressed'); 248 | socket.emit('next') 249 | }; 250 | 251 | //previous on playlist 252 | GPIOButtons.prototype.previous = function() { 253 | //this.logger.info('GPIO-Buttons: previous-button pressed'); 254 | socket.emit('prev') 255 | }; 256 | 257 | //Volume up 258 | GPIOButtons.prototype.volumeUp = function() { 259 | //this.logger.info('GPIO-Buttons: Vol+ button pressed'); 260 | socket.emit('volume','+'); 261 | }; 262 | 263 | //Volume down 264 | GPIOButtons.prototype.volumeDown = function() { 265 | //this.logger.info('GPIO-Buttons: Vol- button pressed\n'); 266 | socket.emit('volume','-'); 267 | }; 268 | 269 | //shutdown 270 | GPIOButtons.prototype.shutdown = function() { 271 | // this.logger.info('GPIO-Buttons: shutdown button pressed\n'); 272 | this.commandRouter.shutdown(); 273 | }; 274 | -------------------------------------------------------------------------------- /app/plugins/system_controller/gpio-buttons/UIConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": { 3 | "label": "GPIO Buttons" 4 | }, 5 | "sections": [ 6 | { 7 | "id": "gpio_setup", 8 | "element": "section", 9 | "label": "Setup your GPIO button actions", 10 | "icon": "fa-plug", 11 | "onSave": {"type":"controller", "endpoint":"system_controller/gpio-buttons", "method":"saveConfig"}, 12 | "saveButton": { 13 | "label": "Save", 14 | "data": [ 15 | "playPauseEnabled", 16 | "playPausePin", 17 | "volumeUpEnabled", 18 | "volumeUpPin", 19 | "volumeDownEnabled", 20 | "volumeDownPin", 21 | "previousEnabled", 22 | "previousPin", 23 | "nextEnabled", 24 | "nextPin", 25 | "shutdownEnabled", 26 | "shutdownPin" 27 | ] 28 | }, 29 | "content": [ 30 | { 31 | "id": "playPauseEnabled", 32 | "element": "switch", 33 | "label": "Enable Play/pause", 34 | "value": false 35 | }, 36 | { 37 | "id": "playPausePin", 38 | "element": "select", 39 | "label": "GPIO Pin", 40 | "value": {"value": 0, 41 | "label": "0" 42 | }, 43 | 44 | "options": [ 45 | { 46 | "value": 2, 47 | "label": "2" 48 | }, 49 | { 50 | "value": 3, 51 | "label": "3" 52 | }, 53 | { 54 | "value": 4, 55 | "label": "4" 56 | }, 57 | { 58 | "value": 5, 59 | "label": "5" 60 | }, 61 | { 62 | "value": 6, 63 | "label": "6" 64 | }, 65 | { 66 | "value": 7, 67 | "label": "7" 68 | }, 69 | { 70 | "value": 8, 71 | "label": "8" 72 | }, 73 | { 74 | "value": 9, 75 | "label": "9" 76 | }, 77 | { 78 | "value": 10, 79 | "label": "10" 80 | }, 81 | { 82 | "value": 11, 83 | "label": "11" 84 | }, 85 | { 86 | "value": 12, 87 | "label": "12" 88 | }, 89 | { 90 | "value": 13, 91 | "label": "13" 92 | }, 93 | { 94 | "value": 14, 95 | "label": "14" 96 | }, 97 | { 98 | "value": 15, 99 | "label": "15" 100 | }, 101 | { 102 | "value": 16, 103 | "label": "16" 104 | }, 105 | { 106 | "value": 17, 107 | "label": "17" 108 | }, 109 | { 110 | "value": 18, 111 | "label": "18" 112 | }, 113 | { 114 | "value": 19, 115 | "label": "19" 116 | }, 117 | { 118 | "value": 20, 119 | "label": "20" 120 | }, 121 | { 122 | "value": 21, 123 | "label": "21" 124 | }, 125 | { 126 | "value": 22, 127 | "label": "22" 128 | }, 129 | { 130 | "value": 23, 131 | "label": "23" 132 | }, 133 | { 134 | "value": 24, 135 | "label": "24" 136 | }, 137 | { 138 | "value": 25, 139 | "label": "25" 140 | }, 141 | { 142 | "value": 26, 143 | "label": "26" 144 | }, 145 | { 146 | "value": 27, 147 | "label": "27" 148 | } 149 | ], 150 | "visibleIf": { 151 | "field": "playPauseEnabled", 152 | "value": true 153 | } 154 | }, 155 | { 156 | "id": "volumeUpEnabled", 157 | "element": "switch", 158 | "label": "Enable Vol+", 159 | "value": false 160 | }, 161 | { 162 | "id": "volumeUpPin", 163 | "element": "select", 164 | "label": "GPIO Pin", 165 | "value": {"value": 0, 166 | "label": "0" 167 | }, 168 | 169 | "options": [ 170 | { 171 | "value": 2, 172 | "label": "2" 173 | }, 174 | { 175 | "value": 3, 176 | "label": "3" 177 | }, 178 | { 179 | "value": 4, 180 | "label": "4" 181 | }, 182 | { 183 | "value": 5, 184 | "label": "5" 185 | }, 186 | { 187 | "value": 6, 188 | "label": "6" 189 | }, 190 | { 191 | "value": 7, 192 | "label": "7" 193 | }, 194 | { 195 | "value": 8, 196 | "label": "8" 197 | }, 198 | { 199 | "value": 9, 200 | "label": "9" 201 | }, 202 | { 203 | "value": 10, 204 | "label": "10" 205 | }, 206 | { 207 | "value": 11, 208 | "label": "11" 209 | }, 210 | { 211 | "value": 12, 212 | "label": "12" 213 | }, 214 | { 215 | "value": 13, 216 | "label": "13" 217 | }, 218 | { 219 | "value": 14, 220 | "label": "14" 221 | }, 222 | { 223 | "value": 15, 224 | "label": "15" 225 | }, 226 | { 227 | "value": 16, 228 | "label": "16" 229 | }, 230 | { 231 | "value": 17, 232 | "label": "17" 233 | }, 234 | { 235 | "value": 18, 236 | "label": "18" 237 | }, 238 | { 239 | "value": 19, 240 | "label": "19" 241 | }, 242 | { 243 | "value": 20, 244 | "label": "20" 245 | }, 246 | { 247 | "value": 21, 248 | "label": "21" 249 | }, 250 | { 251 | "value": 22, 252 | "label": "22" 253 | }, 254 | { 255 | "value": 23, 256 | "label": "23" 257 | }, 258 | { 259 | "value": 24, 260 | "label": "24" 261 | }, 262 | { 263 | "value": 25, 264 | "label": "25" 265 | }, 266 | { 267 | "value": 26, 268 | "label": "26" 269 | }, 270 | { 271 | "value": 27, 272 | "label": "27" 273 | } 274 | ], 275 | "visibleIf": { 276 | "field": "volumeUpEnabled", 277 | "value": true 278 | } 279 | }, 280 | { 281 | "id": "volumeDownEnabled", 282 | "element": "switch", 283 | "label": "Enable Vol-", 284 | "value": false 285 | }, 286 | { 287 | "id": "volumeDownPin", 288 | "element": "select", 289 | "label": "GPIO Pin", 290 | "value": {"value": 0, 291 | "label": "0" 292 | }, 293 | 294 | "options": [ 295 | { 296 | "value": 2, 297 | "label": "2" 298 | }, 299 | { 300 | "value": 3, 301 | "label": "3" 302 | }, 303 | { 304 | "value": 4, 305 | "label": "4" 306 | }, 307 | { 308 | "value": 5, 309 | "label": "5" 310 | }, 311 | { 312 | "value": 6, 313 | "label": "6" 314 | }, 315 | { 316 | "value": 7, 317 | "label": "7" 318 | }, 319 | { 320 | "value": 8, 321 | "label": "8" 322 | }, 323 | { 324 | "value": 9, 325 | "label": "9" 326 | }, 327 | { 328 | "value": 10, 329 | "label": "10" 330 | }, 331 | { 332 | "value": 11, 333 | "label": "11" 334 | }, 335 | { 336 | "value": 12, 337 | "label": "12" 338 | }, 339 | { 340 | "value": 13, 341 | "label": "13" 342 | }, 343 | { 344 | "value": 14, 345 | "label": "14" 346 | }, 347 | { 348 | "value": 15, 349 | "label": "15" 350 | }, 351 | { 352 | "value": 16, 353 | "label": "16" 354 | }, 355 | { 356 | "value": 17, 357 | "label": "17" 358 | }, 359 | { 360 | "value": 18, 361 | "label": "18" 362 | }, 363 | { 364 | "value": 19, 365 | "label": "19" 366 | }, 367 | { 368 | "value": 20, 369 | "label": "20" 370 | }, 371 | { 372 | "value": 21, 373 | "label": "21" 374 | }, 375 | { 376 | "value": 22, 377 | "label": "22" 378 | }, 379 | { 380 | "value": 23, 381 | "label": "23" 382 | }, 383 | { 384 | "value": 24, 385 | "label": "24" 386 | }, 387 | { 388 | "value": 25, 389 | "label": "25" 390 | }, 391 | { 392 | "value": 26, 393 | "label": "26" 394 | }, 395 | { 396 | "value": 27, 397 | "label": "27" 398 | } 399 | ], 400 | "visibleIf": { 401 | "field": "volumeDownEnabled", 402 | "value": true 403 | } 404 | }, 405 | { 406 | "id": "previousEnabled", 407 | "element": "switch", 408 | "label": "Enable Previous", 409 | "value": false 410 | }, 411 | { 412 | "id": "previousPin", 413 | "element": "select", 414 | "label": "GPIO Pin", 415 | "value": {"value": 0, 416 | "label": "0" 417 | }, 418 | 419 | "options": [ 420 | { 421 | "value": 2, 422 | "label": "2" 423 | }, 424 | { 425 | "value": 3, 426 | "label": "3" 427 | }, 428 | { 429 | "value": 4, 430 | "label": "4" 431 | }, 432 | { 433 | "value": 5, 434 | "label": "5" 435 | }, 436 | { 437 | "value": 6, 438 | "label": "6" 439 | }, 440 | { 441 | "value": 7, 442 | "label": "7" 443 | }, 444 | { 445 | "value": 8, 446 | "label": "8" 447 | }, 448 | { 449 | "value": 9, 450 | "label": "9" 451 | }, 452 | { 453 | "value": 10, 454 | "label": "10" 455 | }, 456 | { 457 | "value": 11, 458 | "label": "11" 459 | }, 460 | { 461 | "value": 12, 462 | "label": "12" 463 | }, 464 | { 465 | "value": 13, 466 | "label": "13" 467 | }, 468 | { 469 | "value": 14, 470 | "label": "14" 471 | }, 472 | { 473 | "value": 15, 474 | "label": "15" 475 | }, 476 | { 477 | "value": 16, 478 | "label": "16" 479 | }, 480 | { 481 | "value": 17, 482 | "label": "17" 483 | }, 484 | { 485 | "value": 18, 486 | "label": "18" 487 | }, 488 | { 489 | "value": 19, 490 | "label": "19" 491 | }, 492 | { 493 | "value": 20, 494 | "label": "20" 495 | }, 496 | { 497 | "value": 21, 498 | "label": "21" 499 | }, 500 | { 501 | "value": 22, 502 | "label": "22" 503 | }, 504 | { 505 | "value": 23, 506 | "label": "23" 507 | }, 508 | { 509 | "value": 24, 510 | "label": "24" 511 | }, 512 | { 513 | "value": 25, 514 | "label": "25" 515 | }, 516 | { 517 | "value": 26, 518 | "label": "26" 519 | }, 520 | { 521 | "value": 27, 522 | "label": "27" 523 | } 524 | ], 525 | "visibleIf": { 526 | "field": "previousEnabled", 527 | "value": true 528 | } 529 | }, 530 | { 531 | "id": "nextEnabled", 532 | "element": "switch", 533 | "label": "Enable Next", 534 | "value": false 535 | }, 536 | { 537 | "id": "nextPin", 538 | "element": "select", 539 | "label": "GPIO Pin", 540 | "value": {"value": 0, 541 | "label": "0" 542 | }, 543 | 544 | "options": [ 545 | { 546 | "value": 2, 547 | "label": "2" 548 | }, 549 | { 550 | "value": 3, 551 | "label": "3" 552 | }, 553 | { 554 | "value": 4, 555 | "label": "4" 556 | }, 557 | { 558 | "value": 5, 559 | "label": "5" 560 | }, 561 | { 562 | "value": 6, 563 | "label": "6" 564 | }, 565 | { 566 | "value": 7, 567 | "label": "7" 568 | }, 569 | { 570 | "value": 8, 571 | "label": "8" 572 | }, 573 | { 574 | "value": 9, 575 | "label": "9" 576 | }, 577 | { 578 | "value": 10, 579 | "label": "10" 580 | }, 581 | { 582 | "value": 11, 583 | "label": "11" 584 | }, 585 | { 586 | "value": 12, 587 | "label": "12" 588 | }, 589 | { 590 | "value": 13, 591 | "label": "13" 592 | }, 593 | { 594 | "value": 14, 595 | "label": "14" 596 | }, 597 | { 598 | "value": 15, 599 | "label": "15" 600 | }, 601 | { 602 | "value": 16, 603 | "label": "16" 604 | }, 605 | { 606 | "value": 17, 607 | "label": "17" 608 | }, 609 | { 610 | "value": 18, 611 | "label": "18" 612 | }, 613 | { 614 | "value": 19, 615 | "label": "19" 616 | }, 617 | { 618 | "value": 20, 619 | "label": "20" 620 | }, 621 | { 622 | "value": 21, 623 | "label": "21" 624 | }, 625 | { 626 | "value": 22, 627 | "label": "22" 628 | }, 629 | { 630 | "value": 23, 631 | "label": "23" 632 | }, 633 | { 634 | "value": 24, 635 | "label": "24" 636 | }, 637 | { 638 | "value": 25, 639 | "label": "25" 640 | }, 641 | { 642 | "value": 26, 643 | "label": "26" 644 | }, 645 | { 646 | "value": 27, 647 | "label": "27" 648 | } 649 | ], 650 | "visibleIf": { 651 | "field": "nextEnabled", 652 | "value": true 653 | } 654 | }, 655 | { 656 | "id": "shutdownEnabled", 657 | "element": "switch", 658 | "label": "Enable Shutdown", 659 | "value": false 660 | }, 661 | { 662 | "id": "shutdownPin", 663 | "element": "select", 664 | "label": "GPIO Pin", 665 | "value": {"value": 0, 666 | "label": "0" 667 | }, 668 | 669 | "options": [ 670 | { 671 | "value": 2, 672 | "label": "2" 673 | }, 674 | { 675 | "value": 3, 676 | "label": "3" 677 | }, 678 | { 679 | "value": 4, 680 | "label": "4" 681 | }, 682 | { 683 | "value": 5, 684 | "label": "5" 685 | }, 686 | { 687 | "value": 6, 688 | "label": "6" 689 | }, 690 | { 691 | "value": 7, 692 | "label": "7" 693 | }, 694 | { 695 | "value": 8, 696 | "label": "8" 697 | }, 698 | { 699 | "value": 9, 700 | "label": "9" 701 | }, 702 | { 703 | "value": 10, 704 | "label": "10" 705 | }, 706 | { 707 | "value": 11, 708 | "label": "11" 709 | }, 710 | { 711 | "value": 12, 712 | "label": "12" 713 | }, 714 | { 715 | "value": 13, 716 | "label": "13" 717 | }, 718 | { 719 | "value": 14, 720 | "label": "14" 721 | }, 722 | { 723 | "value": 15, 724 | "label": "15" 725 | }, 726 | { 727 | "value": 16, 728 | "label": "16" 729 | }, 730 | { 731 | "value": 17, 732 | "label": "17" 733 | }, 734 | { 735 | "value": 18, 736 | "label": "18" 737 | }, 738 | { 739 | "value": 19, 740 | "label": "19" 741 | }, 742 | { 743 | "value": 20, 744 | "label": "20" 745 | }, 746 | { 747 | "value": 21, 748 | "label": "21" 749 | }, 750 | { 751 | "value": 22, 752 | "label": "22" 753 | }, 754 | { 755 | "value": 23, 756 | "label": "23" 757 | }, 758 | { 759 | "value": 24, 760 | "label": "24" 761 | }, 762 | { 763 | "value": 25, 764 | "label": "25" 765 | }, 766 | { 767 | "value": 26, 768 | "label": "26" 769 | }, 770 | { 771 | "value": 27, 772 | "label": "27" 773 | } 774 | ], 775 | "visibleIf": { 776 | "field": "shutdownEnabled", 777 | "value": true 778 | } 779 | } 780 | ] 781 | } 782 | ] 783 | } 784 | --------------------------------------------------------------------------------