-
Notifications
You must be signed in to change notification settings - Fork 3
5. Advanced customization, none of the other choices work
sometimes none of the the choices seem to work, for example the compliments module
in javascript, the list of compliments is an object '{....}', which is usually fixed in size
compliments: {
anytime: [..,''..],
morning: [..,..,..],
afternoon: [..,..,..],
evening: [..,..,..],
"....-01-01": [..,..,..]
}
but in reality the structure is an extendable list, more like an array '[...]', but arrays in json have a different structure
[ fieldname: field_value, fieldname: field_value ]
the compliments object key (anytime, morning...) isn't named..
so, how can we get from one format to the other? a workable format might be an array of objects
{
"when":"anytime",
"list": [...,...,...]
}
the schema might look like this
"compliments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"when": {
"type": "string"
},
"list": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
and the config data like this
"compliments": [
{
"when": "anytime",
"list": [
"Hey there sexy!"
]
},
{
"when": "morning",
"list": [
"Good morning, handsome!",
"Enjoy your day!",
"How was your sleep?"
]
},
{
"when": "afternoon",
"list": [
"Hello, beauty!",
"You look sexy!",
"Looking good today!"
]
},
{
"when": "evening",
"list": [
"Wow, you look hot!",
"You look nice!",
"Hi, sexy!"
]
},
{
"when": "....-01-01",
"list": [
"Happy new year!"
]
}
]
we can't change the module config format in config.js as we would have to rewrite the module code
we CAN make a custom schema, and just need to convert the config/defaults values to this form format and back to config.js format
enter the converter script in js a new file, named MMM-Config_converter.js , located in the module folder, same location as the schema file
// you MUST convert all the multiple module config data items that need converting in this one function call
// the complete config: object is passed in and the modified version is expected on returned
some_function_name: function(config_data, direction){
if(direction == 'toForm'){
// here you would do whatever conversions are required for the data
// in compliments , we need to change the object to an array
let new_compliments = []
Object.keys(config_data.compliments).forEach(when=>{
// we have the object key
// now we need to create a little 'object' for each element in the array
// so we will add to the array for each entry in the object
new_compliments.push(
{
// the schema says the element has a when value (the anytime....)
"when":when,
"list":config_data.compliments[when] // and a list value (the stuff to the right of the ':')
}
)
})
// done processing all the entries in the config format object
// now update the passed in config data
// we want the data to survive, so cant be local, the JSON library will let us make a copy
config_data.compliments = JSON.parse(JSON.stringify(new_compliments))
}
else if direction == 'toConfig'){
// we need to go from form format (array), back to expected config.js format object
// setup the empty object
let config_compliments = {}
// loop thru each array element
confg_data.compliments.forEach(element=>{
// create a keyed entry in the old format, by using the two parts of the array entry
config_compliments[element.when] = element.list
})
// all done with the array
// save the modified data
config_data.compliments = JSON.parse(JSON.stringify(config__compliments))
}
return confg_data // modified
}
// this line is critical, we need to tell MMM-Config what the function is
// MMM-Config EXPECTS the name to be 'converter', so the export allows you to name your function
// any way you like
exports.converter=some_function_name
see the final version for the compliments module in the MMM-Config/schemas folder
then you can create a custom form section (in the schema.json file (section schema, form, value )) note: compliments supports multiple instances in config.js so THAT is an array too..
here is the config section of the module definition,
**comments are not allowed in json but I will put them here for some better explanation**
"title": "config",
"items": [
{
"type": "array",
"title": "compliments",
"deleteCurrent": false, // if you want the user to delete ANY item in the list, not just the last set to true (default)
"draggable":false, // if you want the user to be able to reorganize the list, set to true (default)
"items": {
"type": "fieldset", // collection of fields with header
"items": [ // start of list of fields to show in this collection
// the field display will be taken from the schema definition , string, number, .....
{
"title": "when to show",
"key": "compliments.config.compliments[].when" // where to get/set the data for this field
},
{
"type": "array",
"title":"list of phases to show for this time",
"deleteCurrent": false, // same as above
"draggable":false, // same as above
"htmlClass":"compliments_list", // in this case I want a custom field class so I can address it in css
"items": [
{
"notitle": true, // dont display any title over this entry
"deleteCurrent": false, // same as above.. altho this might be useful as true, it DOES add a new separate row with the delete button on EACH element
"key": "compliments.config.compliments[].list[]"
}
]
}
]
}
},
so the customized compliment format in the form looks like this , with the add/remove buttons for the list of phrases
and at the bottom of this section is another add/remove, for the adding another 'when'
the custom date format YYY-MM-DD doesn't work properly.. the schema says there is ONE format ....-01-01, but really, thats just one of many. the form handler can't match data (....-04-03 with ....-01-01)
SO.. if we had ANOTHER form field that we could use for the actual data entry, then the drop down selection for date-format or date-time format could expose the other field for data entry, and our conversion script could handle the changes
note that we are using the JSONFORM titlemap feature to make understandable choices, while maintaining the technical settings underneith
so the schema and form sections get some improvements
"type": "array",
"items": {
"type": "object",
"properties": {
"when": {
"type": "string",
"enum":[
"anytime",
"morning",
"afternoon",
"evening",
"date-format",
"date-time-format",
"weather-format"
]
},
"date-format":{
"type":"string"
},
"date-time-format":{
"type":"string"
},
"weather-format":{
"type":"string",
"enum":[
"day_sunny",
"day_cloudy",
"cloudy",
"cloudy_windy",
"showers",
"rain",
"thunderstorm",
"snow",
"fog",
"night_clear",
"night_cloudy",
"night_showers",
"night_rain",
"night_thunderstorm",
"night_snow",
"night_alt_cloudy_windy"
]
},
"list": {
"type": "array",
"items": {
"type": "string"
}
}
"type": "array",
"title": "compliments",
"deleteCurrent": false,
"draggable":false,
"items": {
"type": "fieldset",
"items": [
{
"title": "when to show",
"key": "compliments.config.compliments[].when",
"onChange":"(evt,node)=>{let choices=['date-format','date-time-format'];let value=evt.target.value; let i=0; let index=choices.indexOf(value); var parentElement =$(evt.target).closest('fieldset');choices.forEach(f=>{let target=parentElement.find('div[class$='+f+']');let style=(index != i?'none':'block'); target[0].style.display=style;i++})}",
"titleMap":{
"anytime":"anytime",
"morning":"morning",
"afternoon":"afternoon",
"evening":"evening",
"weather-format":"weather",
"date-format":"date",
"date-time-format":"datetime"
}
},
{
"key": "compliments.config.compliments[].date-format",
"title":"date to show",
"placeholder":"YYYY-MM-DD",
"fieldHtmlClass":"date-format",
"type": "text",
"description": "YYYY-MM-DD, use .(dot) for any value you don't care, for birthday, don't care about year so ....-MM-DD",
"required":true,
"onInput":"(evt,node)=>{let value=evt.target.value;if(!date_validator(value)){evt.target.parentElement.classList.add('fieldError')}else {evt.target.parentElement.classList.remove('fieldError')}}"
},
{
"key": "compliments.config.compliments[].date-time-format",
"title":"date/time to show",
"placeholder":"min hour day_of_month month dow",
"type": "text",
"fieldHtmlClass":"date-time-format",
"description": "see <a href=\"https://crontab.cronhub.io/\">cron schedule creator</a>",
"required":true,
"onInput":"(evt,node)=>{let value=evt.target.value;if(!cron_validator(value)){evt.target.parentElement.classList.add('fieldError')}else {evt.target.parentElement.classList.remove('fieldError')}}"
},
{
"key": "compliments.config.compliments[].weather-format",
"title":"weather type to show",
"fieldHtmlClass":"weather-format",
"required":true
},
now there is a some extra work to do..