Read the statement by Michael Teeuw here.
MMM-config with modules that have sub-configs (MMM-TouchButton)
-
I have recently started using MMM-config. I was unable to determine how to use it with modules that have sub-configs or that it is unaware of, such as MMM-TouchButton.
MMM-TouchButton requires buttons to be defined in the config, but each button has a sub-config with the information about the individual button (such as icon, command/action, etc). By itself, this would not be a huge problem since I have been using MM for years prior to cool new tools like MMM-config. The problem happens when it appears that MMM-config clobbers configs that it did not create, or maybe just configs that it is not aware of (such as these MMM-TouchButton sub-configs).
Since I am new to MMM-config, I may have done something wrong somewhere along the way.
Example of MMM-TouchButton config (entered manually):
module: "MMM-TouchButton", position: "top_left", config: { buttons: [ { name: "Suspend", icon: "fa fa-pause", command: "/usr/bin/systemctl", args: "suspend", }, ] },
Config for MMM-TouchButton after going into MMM-config to change an unrelated setting (such as switching the default clock module from 12-hour time to 24-hour time) without even touching/expanding the MMM-TouchButton settings:
{ module: "MMM-TouchButton", position: "top_left", config: { buttons: [ "[object Object]" ] }, disabled: false, hiddenOnStartup: false, configDeepMerge: false, order: "*", animateIn: "None", animateOut: "None" },
-
Wow, that is great! I was just hoping for it to not reset the module settings. Being able to add the module-specific details is definitely useful. That you and Tom worked the rest of it out between my first post and now is even more amazing!
I set up my MagicMirror, but then leave it alone for a long time. I typically only touch it when an OS or MM update causes an issue. Looking back now, the entire setup process is so much better than it has ever been before!
Thank you for all the ways that MM has become easier and easier. (And thanks to Tom for adding the schema file!)
I meant to add a link to the module that started this discussion, since it has been useful to me: https://github.com/Tom-Hirschberger/MMM-TouchButton I mainly use it for shutdown/sleep/reboot functions, but it is a great way to be able to run an arbitrary command for an MM running on a touchscreen (or by opening the MM page from a different device and clicking the TouchButton).
-
@joey yes, in JS there are no defined structures.
so in this case the code doesn’t match the docdefaults: { animationSpeed: 0, classes: null, buttons: [], addEmptyTitle: false, refreshOnNotification: true, refreshOnlyIfValueChanged: true, notificationDelay: 3000, notificationsAtStart: [] },
buttons is an array []
but we don’t know what is supposed to be INSIDE it (strings, numbers objects???)MMM-Config has to ‘discover’ the variables used by the module and make assumptions when its not clear
BUT the author (or any user) could create a customized form definition for a module so that it could be clearer and more usable
the MMM-Config wiki describes the different choices for this work .
temporarily I added all the variables defined in the doc
then started MM so it generated the definitions file
then i used thecreate_form_for_module.sh MMM-TouchButton
command to create the custom form file
then restored the original MMM-TouchButton.js
in the module foldergit checkout MMM-TouchButton.js
then edited and made some things selection lists vs text entry
create this file in the MMM-TouchButton folder
MMM-Config.schema.json
with this contents{ "schema": { "MMM-TouchButton": { "type": "object", "title": "properties for MMM-TouchButton", "properties": { "module": { "type": "string", "title": "module", "default": "MMM-TouchButton", "readonly": true }, "disabled": { "type": "boolean", "title": "disabled", "default": false }, "position": { "type": "string", "title": "module position", "readonly": "true" }, "classes": { "type": "string", "title": "classes", "default": "" }, "header": { "type": "string", "title": "header", "default": "" }, "hiddenOnStartup": { "type": "boolean", "title": "hiddenOnStartup", "default": false }, "configDeepMerge": { "type": "boolean", "title": "configDeepMerge", "default": false }, "order": { "type": "string", "title": "order", "default": "*" }, "inconfig": { "type": "string", "title": "inconfig", "default": "0" }, "index": { "type": "integer" }, "animateIn": { "type": "string", "enum": [ "None", "bounce", "flash", "pulse", "rubberBand", "shakeX", "shakeY", "headShake", "swing", "tada", "wobble", "jello", "heartBeat", "backInDown", "backInLeft", "backInRight", "backInUp", "bounceIn", "bounceInDown", "bounceInLeft", "bounceInRight", "bounceInUp", "fadeIn", "fadeInDown", "fadeInDownBig", "fadeInLeft", "fadeInLeftBig", "fadeInRight", "fadeInRightBig", "fadeInUp", "fadeInUpBig", "fadeInTopLeft", "fadeInTopRight", "fadeInBottomLeft", "fadeInBottomRight", "flip", "flipInX", "flipInY", "lightSpeedInRight", "lightSpeedInLeft", "rotateIn", "rotateInDownLeft", "rotateInDownRight", "rotateInUpLeft", "rotateInUpRight", "jackInTheBox", "rollIn", "zoomIn", "zoomInDown", "zoomInLeft", "zoomInRight", "zoomInUp", "slideInDown", "slideInLeft", "slideInRight", "slideInUp" ] }, "animateOut": { "type": "string", "enum": [ "None", "backOutDown", "backOutLeft", "backOutRight", "backOutUp", "bounceOut", "bounceOutDown", "bounceOutLeft", "bounceOutRight", "bounceOutUp", "fadeOut", "fadeOutDown", "fadeOutDownBig", "fadeOutLeft", "fadeOutLeftBig", "fadeOutRight", "fadeOutRightBig", "fadeOutUp", "fadeOutUpBig", "fadeOutTopLeft", "fadeOutTopRight", "fadeOutBottomRight", "fadeOutBottomLeft", "flipOutX", "flipOutY", "lightSpeedOutRight", "lightSpeedOutLeft", "rotateOut", "rotateOutDownLeft", "rotateOutDownRight", "rotateOutUpLeft", "rotateOutUpRight", "hinge", "rollOut", "zoomOut", "zoomOutDown", "zoomOutLeft", "zoomOutRight", "zoomOutUp", "slideOutDown", "slideOutLeft", "slideOutRight", "slideOutUp" ] }, "config": { "type": "object", "title": "config", "properties": { "animationSpeed": { "type": "integer" }, "classes": { "type": "string" }, "buttons": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "icon": { "type": "string" }, "imgIcon": { "type": "string" }, "command": { "type": "string" }, "args": { "type": "string" }, "title": { "type": "string" }, "notification": { "type": "string" }, "payload": { "type": "object", "properties": { "type": { "type": "string" }, "payload": { "type": "array", "items": { "type": "string" } } } }, "profiles": { "type": "string" }, "classes": { "type": "string" }, "conditions": { "type": "array", "items": { "type": "object", "properties": { "source": { "type": "string", "enum":[ "out", "err", "code" ] }, "type": { "type": "string", "enum":[ "lt", "le", "gt", "ge", "eq", "incl", "mt" ] }, "value": { "type": "string" }, "icon": { "type": "string" }, "jsonpath": { "type": "string" }, "imgIcon": { "type": "string" }, "classes": { "type": "string" } } } } } } }, "addEmptyTitle": { "type": "boolean" }, "refreshOnNotification": { "type": "boolean" }, "refreshOnlyIfValueChanged": { "type": "boolean" }, "notificationDelay": { "type": "integer" }, "notificationsAtStart": { "type": "array", "items": { "type": "string" } } } } } } }, "form": [ { "key": "MMM-TouchButton.disabled", "htmlClass": "disabled_checkbox", "description": "when checked the module will not be used by MagicMirror<br> but will remain in config.js if already present" }, { "key": "MMM-TouchButton.position", "description": "use Module Positions section below to set or change" }, { "key": "MMM-TouchButton.header", "description": "header to use for this module" }, { "key": "MMM-TouchButton.hiddenOnStartup", "description": "Set module as being hidden on startup. This field is optional." }, { "key": "MMM-TouchButton.configDeepMerge", "description": "Allow to merge with internal configuration in deep" }, { "key": "MMM-TouchButton.classes", "description": "css classes to use for this module, beyond what MM provides" }, { "key": "MMM-TouchButton.order", "type": "hidden" }, { "key": "MMM-TouchButton.inconfig", "type": "hidden" }, { "key": "MMM-TouchButton.animateIn", "title": "animateIn", "description": "select one of these to change the behavior when the module is shown" }, { "key": "MMM-TouchButton.animateOut", "title": "animateOut", "description": "select one of these to change the behavior when the module is hidden" }, { "key": "MMM-TouchButton.index", "type": "hidden" }, { "type": "fieldset", "title": "config", "htmlClass": "moduleConfig", "items": [ { "type": "button", "title": "Open module readme", "htmlClass": "repo_button", "onClick": "(evt,node)=>{let siblings=$(evt.target).siblings('.readme_url');let element=siblings.toArray()[0];let url=element.innerText;let pos=$(evt.target).offset();process_readme(url,pos)}" }, { "type": "button", "htmlClass": "hidden readme_url", "title": "http://localhost:xxxx/modules/MMM-TouchButton/README.md" }, { "title": "animationSpeed", "key": "MMM-TouchButton.config.animationSpeed" }, { "title": "classes", "key": "MMM-TouchButton.config.classes" }, { "type": "array", "title": "buttons", "deleteCurrent": false, "items": { "type": "fieldset", "title": "buttons", "items": [ { "title": "name", "key": "MMM-TouchButton.config.buttons[].name" }, { "title": "icon", "key": "MMM-TouchButton.config.buttons[].icon" }, { "title": "imgIcon", "key": "MMM-TouchButton.config.buttons[].imgIcon" }, { "title": "command", "key": "MMM-TouchButton.config.buttons[].command" }, { "title": "args", "key": "MMM-TouchButton.config.buttons[].args" }, { "title": "title", "key": "MMM-TouchButton.config.buttons[].title" }, { "title": "notification", "key": "MMM-TouchButton.config.buttons[].notification" }, { "type": "fieldset", "title": "payload", "items": [ { "title": "type", "key": "MMM-TouchButton.config.buttons[].payload.type" }, { "type": "array", "title": "payload", "deleteCurrent": false, "items": [ { "title": "payload {{idx}}", "key": "MMM-TouchButton.config.buttons[].payload.payload[]", "type": "ace", "aceMode": "json", "aceTheme": "twilight", "width": "100%", "height": "100px" } ] } ] }, { "title": "profiles", "key": "MMM-TouchButton.config.buttons[].profiles" }, { "title": "classes", "key": "MMM-TouchButton.config.buttons[].classes" }, { "type": "array", "title": "conditions", "deleteCurrent": false, "items": { "type": "fieldset", "title": "conditions", "items": [ { "title": "source", "key": "MMM-TouchButton.config.buttons[].conditions[].source" }, { "title": "type", "key": "MMM-TouchButton.config.buttons[].conditions[].type" }, { "title": "value", "key": "MMM-TouchButton.config.buttons[].conditions[].value" }, { "title": "icon", "key": "MMM-TouchButton.config.buttons[].conditions[].icon" }, { "title": "jsonpath", "key": "MMM-TouchButton.config.buttons[].conditions[].jsonpath" }, { "title": "imgIcon", "key": "MMM-TouchButton.config.buttons[].conditions[].imgIcon" }, { "title": "classes", "key": "MMM-TouchButton.config.buttons[].conditions[].classes" } ] } } ] }, "draggable": false }, { "title": "addEmptyTitle", "key": "MMM-TouchButton.config.addEmptyTitle" }, { "title": "refreshOnNotification", "key": "MMM-TouchButton.config.refreshOnNotification" }, { "title": "refreshOnlyIfValueChanged", "key": "MMM-TouchButton.config.refreshOnlyIfValueChanged" }, { "title": "notificationDelay", "key": "MMM-TouchButton.config.notificationDelay" }, { "type": "array", "title": "notificationsAtStart", "deleteCurrent": false, "items": [ { "title": "notificationsAtStart {{idx}}", "key": "MMM-TouchButton.config.notificationsAtStart[]" } ] } ] } ], "value": { "disabled": true, "module": "MMM-TouchButton", "position": "none", "order": "*", "inconfig": "0", "config": { "animationSpeed": 0, "classes": null, "buttons": [ { "name": "", "icon": "", "imgIcon": null, "command": "", "args": "", "title": null, "notification": "", "payload": { "type": "", "payload": {} }, "profiles": null, "classes": null, "conditions": [ ] } ], "addEmptyTitle": false, "refreshOnNotification": true, "refreshOnlyIfValueChanged": true, "notificationDelay": 3000, "notificationsAtStart": [] }, "animateIn": "none", "animateOut": "none" } }
its not everything that can be done…
headings can be changed,
help can be added
etc… -
i submitted this to the module
-
Tom added the schema file to the module package for future users
-
Wow, that is great! I was just hoping for it to not reset the module settings. Being able to add the module-specific details is definitely useful. That you and Tom worked the rest of it out between my first post and now is even more amazing!
I set up my MagicMirror, but then leave it alone for a long time. I typically only touch it when an OS or MM update causes an issue. Looking back now, the entire setup process is so much better than it has ever been before!
Thank you for all the ways that MM has become easier and easier. (And thanks to Tom for adding the schema file!)
I meant to add a link to the module that started this discussion, since it has been useful to me: https://github.com/Tom-Hirschberger/MMM-TouchButton I mainly use it for shutdown/sleep/reboot functions, but it is a great way to be able to run an arbitrary command for an MM running on a touchscreen (or by opening the MM page from a different device and clicking the TouchButton).
-
@joey we are still discussing how to properly support his conditions settings, so there will be an update when we figure it out
the form library makes a lot of these things simple
add an enum to the string definition and now you get a select list, so a user cant put in the wrong thing, no spelling errors… -
@joey make sure you backup, backup , backup
my backup/restore scripts on top of install/update
can give you a lot of security and safety -
@joey Great you like the module.
Just to clear out some things.Most of the work did Sam not me. I only hit the merge button. So big thank you to him.
-
@sdetweil said in MMM-config with modules that have sub-configs (MMM-TouchButton):
@joey make sure you backup, backup , backup
my backup/restore scripts on top of install/update
can give you a lot of security and safetyI should probably look into that. Currently, I just copy the entire directory to another place on the MM system (in case of an issue with upgrade or messing up the config), and I copy the config.js file to another system (in case of an OS/hardware issue – I use a LOT of old hardware). Up until now, I have mainly taken an OS/hardware failure as an opportunity to try a new flavor of Linux and investigate new modules while setting up MM from scratch again!
I am setting up a new one (brand new laptop with a manufacture date of only 12 years ago!) and it is a great opportunity to look into a proper backup with your scripts.
-
@joey the backup approach gives you the ability to restore the modules and config/css to another system , after install, so you get a migration tool
AND it provides git versioning… so if you need to go back 2 revs you can
it also supports putting the backup on a (private recommended) github repo, so you can restore directly from there and don’t have any local file system failure issues… disk drives, usb sticks, etc
AND it doesn’t matter WHERE you are at the time, -