Add user data to config_schema

If you are asking a question, please follow this template:

  1. My goal is: [Save a user data into config_schema]
  2. My actions are:
    In init.js, using the index.html to save WiFi info using Config.Set
log('Calling /rpc/Config.Set  ...');
    $.ajax({
      url: '/rpc/Config.Set',
      data: JSON.stringify(
        {config: {wifi: 
          {sta: {enable: true, ssid: $('#ssid').val(), pass: $('#pass').val()}}
        }}),
      type: 'POST',
      },
    })
  1. The result I see is: it is working
  2. My expectation & question is:
    Is it possible to add a “User Data” to config_schema which I can store user data ?
    So that, it can be accessed by after bootup.

Your code will execute on a browser once someone connects to the web server in your device and asks for that page. It will POST a JSON object to the RPC Config.Set, which is part of the Config-Service and is described here.
If that is what you want, so far so good.
You can write your own RPC to receive whatever you want and do whatever you want with that; you just need to add a handler. Described here and here
To store something in the config_schema, you define it in the YAML file (mos.yml) and the build process will generate the functions to access it. Look for examples in the libraries and other answers in this forum. Described here.
Once you have defined a key, you can set it and get it, you can operate on it from a RPC call, and you can use the Config-Service on it.

What @scaprile said.
Basically you can set any config that you have defined in your mos.yml
You can also see what is available in your config using the RPC function Config.Get, and you can do this from the mDash console. I’ve found it really useful to test this type of thing in mDash console.

You can test various RPC functions here and see what happens on your device.
If you choose to write your own RPC function it will also turn up here.

Once you are happy with the behaviour you can then write your JavaScript functions

I following the example in
https://mongoose-os.com/docs/mongoose-os/userguide/configuration.md

config_schema:
  - ["hello", "o", {"title": "Hello app settings"}]
  - ["hello.who", "s", "world", {"title": "Who to say hello to"}]

I can access the value by mgos_sys_config_get_hello()
I try changing the value through RPC function call ‘rpc/Config.Set’ which is OK.
However, the value “hello.who” get initialize back to “world” again after reboot.

What is the format I should use to just declare without init value ??
I tries following but did not work

  • [“hello.who”, “s”, “”, {“title”: “Who to say hello to”}]

If my understanding of the Service-Config docs is correct, you have to call Config.Save for changes to be permanent.

Yes. I have saved by calling “rpc/Config.Save”
It works on system parameters like “wifi.sta.ssid” and “wifi.sta.pass”
Value will restore in new value after reboot.

But it will get re-init to the defined value when put under config_schema:

So, I want to know how I can just declare but not initialize with a value on reboot

I find the solution but don’t understand why it works this way.???

When I add one more level, then it works.
Now using the RPC function, I can read back what changes
/rpc/Config.Set
/rpc/Config.Get

config_schema:

  • [“app”, “o”, {“title”: “App settings”}]
  • [“app.user”, “o”, {“title”: “settings”}]
  • [“app.user.data1”, “s”, “test”, {“title”: “data1”}]

with below setting, it does not work…
config_schema:

  • [“app”, “o”, {“title”: “App settings”}]
  • [“app.data1”, “s”, “test”, {“title”: “data1”}]

Did you ensure you set the "save" parameter from the RPC call to true? I’ve been caught out by this one before. It will NOT survive reboot unless you save it. You don’t need to reboot, but you can if you want.

How are you doing the RPC call? mDash? mos call Config.Set? curl??
There are a few gotchas depending on which method you’ve chosen

config_schema:
  - ["app", "o", {}]
  - ["app.user", "o", {}]
  - ["app.user.data1", "s", "data1", {}]
  - ["app.user.data2", "s", "data2", {}]

works very well when Config.Set is called with the correct parameters.

mos --port ws://IP/rpc call config.get '{"key":"app"}'
{
  "user": {
    "data1": "data1",
    "data2": "data2"
  }
}

mos --port ws://IP/rpc call config.set '{"config":{"app":{"user":{"data1":"data1new"}}},"save":true,"reboot":false}'
{
  "saved": true
}

mos --port ws://IP/rpc call config.get '{"key":"app"}'
{
  "user": {
    "data1": "data1new",
    "data2": "data2"
  }
}

PS.

There is no such thing like lower priority YAML file. An application has only one yaml file which is mos.yml

There are other mos.yml files in system and user libraries, all mos.yml files are merged and the application mos.yml in the project root directory is on top. Yes, “higher priority” is not the proper word, it overrides the other mos.yml files in system and user libs.

The OP mentioned he calls Config.Save after Config.Get, that should do for not adding "save":true, does it ?

He also mentions the problem is the new config does not survive reboot

Yes, sure, each library has its own mos.yml and there are some *.yaml files which are pulled in by the build process, but here we were talking about the application level.

If the save and reboot keys are not specified, Config.Set does save and doesn’t reboot.
I’m using custom objects defined in mos.yml and never had a problem.

@dilbert168 should post a simple example to reproduce the issue.

OK, I will withdraw my wrong comments. Thanks for the clarification.

Yes. With your confirm_schema format, it works for me too.
However, it does not work if

config_schema:

  • [“app”, “o”, {}]
  • [“app.data1”, “s”, “data1”, {}]
  • [“app.data2”, “s”, “data2”, {}]

I am using the file in “fs\index.html” to perform the RPC function

$.ajax({
      url: '/rpc/Config.Set',
      data: JSON.stringify({config: {
        wifi: {sta: {enable: true, ssid: $('#ssid').val(), pass: $('#pass').val()}},
        app: {user: {data1: $('#userData').val()}},
      }}),
      type: 'POST',
      success: function(data) {
        $.ajax({url: '/rpc/Config.Get', success: function(data) {log('Result: ' + JSON.stringify(data));}});
        $.ajax({url: '/rpc/Config.Save', type: 'POST', data: JSON.stringify({"reboot": true})});
      },
    });

This line looks wrong to me.

For this to work I would have expected the mos.yml to look like:

config_schema:
* [“app”, “o”, {}]
* [“app.user.data1”, “s”, “data1”, {}]
* [“app.user.data2”, “s”, “data2”, {}]

Given the mos.yml you shared I would have expected it to be:
app: {data1: $('#userData').val()},

This works for me:
In mos.yml:

config_schema:
  - ["debug.level", 2]
  - ["app", "o", {}]
  - ["app.user", "o", {}]
  - ["app.user.data1", "s", "user.data1", {}]
  - ["app.user.data2", "s", "user.data2", {}]
  - ["app.data1", "s", "data1", {}]
  - ["app.data2", "s", "data2", {}]

fs/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Config schema test</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  </head>
  <body>
    <div style="margin:auto">
      <h1 style="text-align:center;" id="room">Config schema test</h1>
      <div class="row form">

        <div class="col c4">
          <div class="form-control">
            <label for="ssid">WiFi Network:</label>
            <input type="text" id="ssid" placeholder="Type WiFi network...">
          </div>
          <div class="form-control">
            <label for="pass">WiFi Password:</label>
            <input type="text" id="pass" placeholder="Type WiFi password...">
          </div>
          <div>
            <label for="appuserdata1">app.user.data1:</label>
            <input type="text" id="appuserdata1"  placeholder="Type app.user.data1">
          </div>
          <div>
            <label for="appuserdata2">app.user.data2:</label>
            <input type="text" id="appuserdata2" placeholder="Type app.user.data2">
          </div>
          <div>
            <label for="appdata1">app.data1:</label>
            <input type="text" id="appdata1" placeholder="Type app.data1">
          </div>
          <div>
            <label for="appdata2">app.data2:</label>
            <input type="text" id="appdata2" placeholder="Type app.data2">
          </div>
          <div class="form-control"><button href="#" class="btn btn-sm btn-c" id="save">Save settings</button></div>
        </div>

        <div class="col c8">
          <!-- <h3>Result:</h3> -->
          <div id="result"></div>
        </div>

      </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  </body>
  <script type="text/javascript">
    $(window).on("load", function () {
      console.log('Window loaded. Calling /rpc/Config.Get  ...');
      $.ajax({url: '/rpc/Config.Get', success: function (data) {
          console.log('Result after Config.Get in window onload: ' + JSON.stringify(data.app));
          $('#ssid').val(data.wifi.sta.ssid);
          $('#pass').val(data.wifi.sta.pass);
          $('#appuserdata1').val(data.app.user.data1);
          $('#appuserdata2').val(data.app.user.data2);
          $('#appdata1').val(data.app.data1);
          $('#appdata2').val(data.app.data2);
        }});
    });
    $('#save').on('click', function () {
      console.log('Calling /rpc/Config.Set  ...');
      $.ajax({
        url: '/rpc/Config.Set',
        type: 'POST',
        data: JSON.stringify({
          config: {
            wifi: {sta: {ssid: $('#ssid').val(), pass: $('#pass').val()}},
            app: {
              user: {
                data1: $('#appuserdata1').val(),
                data2: $('#appuserdata2').val()
              },
              data1: $('#appdata1').val(),
              data2: $('#appdata2').val()
            }
          }
        }),
        success: function (data) {
          $.ajax({url: '/rpc/Config.Get', success: function (data) {
              console.log('Config.Get result after Config.Set: ' + JSON.stringify(data.app));
            }});
          console.log('Success. Rebooting ...');
          $.ajax({
            url: '/rpc/Config.Save',
            type: 'POST',
            data: JSON.stringify({"reboot": true})
          });
        }
      });
    }
    );
  </script>
</html>

First run after mos wifi…

mos --port ws://IP/rpc config-get app
{
  "data1": "data1",
  "data2": "data2",
  "user": {
    "data1": "user.data1",
    "data2": "user.data2"
  }
}

Log in the browser

Result after Config.Get in window onload: {"user":{"data1":"user.data1","data2":"user.data2"},"data1":"data1","data2":"data2"}

Set the parameters in the browser:

app.user.data1:user.data1-1
app.user.data2:user.data2-1
app.data1:data1-1
app.data2:data2-1

Click Save settings (the device will reboot).
Log in the browser after Config.Get:

Config.Get result after Config.Set: {"user":{"data1":"user.data1-1","data2":"user.data2-1"},"data1":"data1-1","data2":"data2-1"}
mos --port ws://IP/rpc config-get app
{
  "data1": "data1-1",
  "data2": "data2-1",
  "user": {
    "data1": "user.data1-1",
    "data2": "user.data2-1"
  }
}

Hope it helps.

Thank you so much for such detail example. Yes, when I follow your configuration, I can get it working too.

But my initial code without the “user” defined all over my code. Then It does NOT work.

  • [“app”, “o”, {}]
  • [“app.data1”, “s”, “data1”, {}]
    Which is OK if there is limitation. I can follow your example for my project.

My example shows that both variants work.