Context

Execution Lifecycle of Saved Scripts

You can save Lua scripts to the device as plugins (extensions).
Saved scripts are loaded and executed at the following times:

  • Upon device startup.
  • When obniz.plugin.reloadLua() is called from the SDK.

For example, in the program below, the device is overwritten with a script that sends "Hello" to the cloud (regardless of whether a Lua script already exists on the device). The reloadLua() function then reloads the script, causing the data to be received once.

When a reload occurs, any Lua script currently running on the device is terminated. All variables and functions are cleared, and the saved Lua script is reloaded from scratch.

obniz.onconnect = async () => {
  obniz.plugin!.onreceive = (data: any, str: string | null) => {
    console.log("received", str);
  };

  // Save the script to the device storage
  obniz.storage.savePluginLua(`
cloud.pluginSend("Hello")
  `);
  
  // Reload and execute the saved script
  obniz.plugin.reloadLua();
}

If the device already contains a Lua script that sends data to the cloud, you can clear the current context and restart the execution simply by calling obniz.plugin.reloadLua() as shown below:

obniz.onconnect = async () => {
  obniz.plugin!.onreceive = (data: any, str: string | null) => {
    console.log("received", str);
    // You will receive "Hello" if the above Lua script is already saved.
  };

  obniz.plugin.reloadLua();
}

On-the-fly Execution

Even when sending and executing Lua scripts from the JavaScript SDK on the fly without saving them internally, the execution Context remains shared and unique.

For instance, assuming there is no Lua script saved on the device, if you run the following program, the variable a is retained in memory. Therefore, a will be incremented every time a=a+1 is executed.

In the program below, onreceive will receive the values 2, 3, and 4 sequentially.

obniz.onconnect = async () => {
  obniz.plugin!.onreceive = (data: any, str: string | null) => {
    console.log("received", str);
    // You will receive 2, 3, 4
  };

  obniz.plugin!.execLua("a=1");
  obniz.plugin!.execLua(`a=a+1;cloud.pluginSend(tostring(a))`);
  obniz.plugin!.execLua(`a=a+1;cloud.pluginSend(tostring(a))`);
  obniz.plugin!.execLua(`a=a+1;cloud.pluginSend(tostring(a))`);
}

In addition to variables, you can also transfer functions. If a function with the same name already exists, it will be overwritten.

obniz.onconnect = async () => {
  obniz.plugin!.onreceive = (data: any, str: string | null) => {
    console.log("received", str);
  };

  obniz.plugin!.execLua("a=1");
  obniz.plugin!.execLua(`
function add(value)
  return value + 1
end
a=add(a);
cloud.pluginSend(tostring(a))
  `);
}

The execLua() method is equivalent to the eval() function in JavaScript.
The code passed to it is not saved to the device's internal storage; it is executed only for that specific moment.

However, the Lua environment itself continues to run inside the device. The Context (memory state) is maintained consistently until the device restarts or reloadLua() is called. This means any changes made to variables or functions will persist throughout the session.

Coexistence of Saved Lua and execLua()

The Lua script saved in the device (which runs automatically after startup) and the Lua code executed via execLua() share the same Context.

This allows you to use execLua() to modify variables or functions defined in the pre-installed Lua script.

Note that these modifications only affect the current runtime state. If the device is restarted or reloadLua() is called, the memory is cleared, and the original Lua script saved in the storage will be executed again from its initial state.

In the example below, a variable a is initialized to 0 and an increment() function is saved to the device. Although this save operation only needs to be done once, this script ensures it is rewritten upon every connection.

Then, execLua() is used to call the increment() function (stored internally) to increase a and send the result to the cloud.

obniz.onconnect = async () => {
  obniz.plugin!.onreceive = (data: any, str: string | null) => {
    console.log("received", str);
  };

  // Saving the base script to storage
  obniz.storage.savePluginLua(`
a=0
function increment()
  a=a+1
  return a
end
  `);
  obniz.plugin.reloadLua();

  // Executing a command that uses the saved function
  obniz.plugin!.execLua(`
increment();
cloud.pluginSend(tostring(a))
  `);
}

Since the Context is persistent, executing the command multiple times will cause the returned value to keep increasing.

obniz.onconnect = async () => {
  obniz.plugin!.onreceive = (data: any, str: string | null) => {
    console.log("received", str);
  };

  obniz.storage.savePluginLua(`
a=0
function increment()
  a=a+1
  return a
end
  `);
  obniz.plugin.reloadLua();

  // You will receive 1
  obniz.plugin!.execLua(`
increment();
cloud.pluginSend(tostring(a))
  `);

  // You will receive 2
  obniz.plugin!.execLua(`
increment();
cloud.pluginSend(tostring(a))
  `);
}