← All solutions
Caelestia Shell · Cache Persistence

Persistent Data Caching
in Shell Services

How I implemented file-based JSON caching for Weather.qml and NetworkUsage.qml. This resolves the annoying issue where weather stats and monthly network bandwidth values reset to zero whenever Caelestia Shell or Quickshell is restarted.

Caelestia Shell Quickshell / QML Weather & Network Services
The Challenge

In Quickshell, singletons (like Weather.qml and NetworkUsage.qml) run in a volatile QML engine. This leads to two specific issues:

  • Weather resets: On boot or shell reload, the weather widget displays 0°C / No weather until the next HTTP fetch completes.
  • Network usage resets: Total download/upload metrics in the performance panel fall back to 0 bytes because initial session bytes (calculated from /proc/net/dev) are cleared.
Design & Limitations

Quickshell provides the FileView type for reading files, but it is read-only and its .text property must be accessed as a function: text().

To write cache updates to disk safely in background threads without blocking QML animations, I employ a one-liner Python process launched asynchronously via Quickshell.execDetached().

Implementation Details

Here is how the caching mechanisms are structured for both components:

1. Asynchronous Write & Read Loop (Weather)

Inside Weather.qml, the JSON string retrieved from the API is written asynchronously using Python. On component completion, FileView loads the cached data before the new network request triggers:

qml
// Writing to cache
Quickshell.execDetached(["python3", "-c",
    "import sys; open(sys.argv[1], 'w').write(sys.argv[2])",
    cachePath, jsonPayload
]);

// Reading from cache
FileView {
    id: cacheReader
    source: "file://" + cachePath
    onTextChanged: {
        const data = JSON.parse(text());
        root.cc = data.cc;
        root.forecast = data.forecast;
    }
}

2. High I/O Avoidance & Monthly Cycles (Network Usage)

Since /proc/net/dev updates every second, writing network usage to disk immediately would cause high disk write amplification. To solve this:

  • I use a 30-second Timer to periodically dump network statistics to network_usage.json.
  • A monthly validation check is implemented. The cache is only validated if the stored date matches the current system month (YYYY-MM), implementing automatic monthly resets.
qml
// Inside NetworkUsage.qml
Timer {
    interval: 30000 // 30 seconds
    repeat: true
    running: true
    onTriggered: {
        let serialized = {
            month: getCurrentMonthString(),
            downloadTotal: root.downloadTotal,
            uploadTotal: root.uploadTotal
        };
        saveCache(JSON.stringify(serialized));
    }
}
The Python one-liner approach keeps the QML side clean: no native C++ plugin needed, no extra dependencies. The process is launched detached so it never blocks the animation thread, even on slow spinning disks.