Synchronize config changes across devices

Synced configuration changes

This is a small but potentially useful feature if you have multiple devices and what to make changes to a setup’s configuration become effective at the exact moment in time without communication between devices.

Consider this example: You have a video wall with 4 displays. If you assign a new video that that wall, one device might be faster at downloading the video that all other devices. If that happens the one device switches to the new configuration before all other devices and begins showing the new video while the other device still how the old video.

In the optimal case you want to have your wall updated at the exact same time so your assigned content is consistent and properly shows across all devices.

There wasn’t any way to do that easily before today. Your only option was to build a custom communication protocol between your devices and have them coordinate the change somehow.

sync_ts

There is now a new value in the __metadata section within the automatically created config.json file. This numerical value is a timestamp and is automatically created by the info-beamer backend for each setup. It represents the timestamp of when the setup has last been updated. It is guaranteed to be identical across all devices sharing the same setup once an updated configuration has been synced to all devices running that setup. For technical reasons the timestamp isn’t exact and might be up to 30 seconds into the future. For the intended use case that’s not a problem though.

Here’s how you can use this to sync configuration changes:

  • In your node.lua file you use util.json_watch as usual to detect an updated configuration.
  • Instead of applying the configuration immediately you query the value of sync_ts within the __metadata section and add (for example) 60 seconds. You store this timestamp together with the new configuration in a variable for future use.
  • You set up a callback for each frame and check if the calculated timestamp has been reached
  • If so, you now apply the saved configuration

By doing that, the configuration is applied across devices at the same time without any additional communication required.

Of course this technique is still somewhat limited and intended to make synced changes more likely, but it cannot enforce them of course:

  • If one device is very slow in downloading newly assigned content, the timestamp calculated might already be in the past once the new configuration file is updated on the device. In that case the config should be applied immediately. You can optimize for that case by enabling the peer to peer feature for faster downloads for devices sharing the same network.
  • The 60 seconds in the example above result in configuration changes taking at least that many seconds before you see the result of editing a setup’s configuration in the dashboard. But the higher the value the more likely it is that the change is synced. So depending on what’s important for your setup and the content you show you can choose between faster updates and more synchronized updates.

Example code

Handle updated config.json files and store the new configuration and a timestamp of when to activate it:

local config_switch, config

util.json_watch("config.json", function(new_config)
    if new_config.synced_changes == 0 or not config then
        config_switch = os.time()
    else
        config_switch = new_config.__metadata.sync_ts +
                        config.synced_changes
    end
    config = new_config
end)

Set up a new tick event that gets called each frame. Inside we can check if the config switch time has been reached and apply the configuration in that case. Your usual configuration handling can then be placed in apply_config similar to how it worked in util.json_watch before:

node.event("tick", function()
    if config_switch and os.time() >= config_switch then
        config_switch = nil
        apply_config(config)
    end
end)

The corresponding configuration item in node.json might look like this:

{
    "title": "Synchronize changes",
    "ui_width": 6,
    "name": "synced_changes",
    "type": "select",
    "hint": "Try to make changes appear on all devices at the same time",
    "options": [
        [0, "Update as fast as possible"],
        [45, "Wait around 60 seconds"],
        [105, "Wait around 120 seconds"]
    ],
    "default": 0
}