How to turn off the §$%& caching

Hi there,

I use Lovelace / floorplan, where the SVG is not showing an actual floor but some other content. Now, that SVG file (stored locally in config/www) may be updated by a tap_action which starts a server-side Perl script, which extracts relevant information from an external web site. So far, so good.

Once the perl script is done, the trouble starts:

  • the browser does not automatically refresh the page - ok, I can live with that, it’s just another tap
  • the browser caches the SVG file - ok, this is configurable in the browser
  • the home assistant web server is caching the SVG file and does not see it has been updated.

I have surfed the web, asked ChatGPT, looked in this form, tried out every solution I could find. Nothing works.

Any help is appreciated!
Best regards,
Fry

Edit: since most of my setup refers to locally stored SVGs, I could live with switching off the home assistant caching completely. Although a solution that effects only this one floorplan SVG would be preferably. But I don’t know how to do either of these.

I don’t have a solution to disabling the caching, but the only solution I have found for updating an image on the dashboard has been to change the URL of the image by adding a query parameter at the end - e.g. “/floorplan/home.svg?1” This must be done every time it changes, which means you’d need some kind of automated solution for changing the YAML as well.

1 Like

Thanks, I’ll try that.
Just for my understanding, are you suggesting that I configure Lovelace like this:

  - title: MyTitle
    path: mypath
    panel: true
    type: panel
    badges: []
    cards:
      - type: custom:floorplan-card
        config:
          image: /local/my_volatile.svg?${ now().strftime('%Y%m%d%H%M') }
          stylesheet: /local/floorplan-card-style.css

This does not work. Even clearing the browser cache and reloading, I get the same outdated SVG floorplan.

Probably because Lovelace does not support templates, so the string it gets for the file name (including the ?.. part) is the same each time, and it uses the cached SVG then.

It is strange and annoying that something so simple like a forced cache refresh is made so hard or even impossible.

The query solution is used to force browser’s to read the updated file and not use its cache.

It is not for server side caching.

Is there a way to switch off server-side caching. Or at least make it cache for only a certain time, e.g. 5 min.

I have found a really crude workaround, and it seems to work for my purpose although it is far from perfect. I’m just describing it here so others may refine it or correct me if I am wrong.

I have a docker installation of Home Assistant. This installation appears to be using the aiohttp.py Python-module as a builtin web server. I logged into the running docker container using

docker exec -it visu bash

(visu is the name of my home assistant container) and once inside the container, did a

cd /usr/src/homeassistant/homeassistant/components/http/
vi static.py

The file static.py seems to be a core script that plays a role in serving static files.

Inside the vi editor, to turn off browser caching, I changed the line

CACHE_TIME: Final = 31 * 86400

into

CACHE_TIME: Final = 10

Further down in static.py, there seems to be an important point in the code where it says

            if (filepath := PATH_CACHE.get(key)) is None:
                filepath = PATH_CACHE[key] = await hass.async_add_executor_job(
                    _get_file_path, filename, self._directory, self._follow_symlinks
                )

I am not really fluent in Python - but this seems to me to be the check if a desired file is already in the cache: If yes, serve it from there - the “:=” retrieves the key for that. And if no, store it there - that is apparent from the double assignment below the if-line.

So I simply deleted the part PATH_CACHE[key] = below the if-line.

With this, the server should no longer cache any static file. My first checks seem to indicate that this works.

Of course, at the cost that NO static file is cached anymore. Not elegant.

I wanted to refine this, for example, it would be nice to just apply this to files that contain the word “volatile” in their filename. But anything I tried in that way led to the Home Assistant docker container not restarting properly.

So this is a shallow hack, no more. I wish the power users and developers would come up with something better… I have not found out how to refine this.

Can somebody help, point me in the right direction?

1 Like

I was able to improve on the previous solution.

In the docker container, in the file /usr/src/homeassistant/homeassistant/components/http/static.py:

  1. Replacing
    CACHE_TIME: Final = 31 * 86400
    by
    CACHE_TIME: Final = 1
    and
  2. replacing
if (filepath := PATH_CACHE.get(key)) is None:
                filepath = PATH_CACHE[key] = await hass.async_add_executor_job(
                    _get_file_path, filename, self._directory, self._follow_symlinks
                )

by

if 'volatile' not in str(filename) and (filepath := PATH_CACHE.get(key)) is None:
                filepath = PATH_CACHE[key] = await hass.async_add_executor_job(
                    _get_file_path, filename, self._directory, self._follow_symlinks
                )
else
                filepath = await hass.async_add_executor_job(
                    _get_file_path, filename, self._directory, self._follow_symlinks
                )

seem to do the job for me.

→ Any file whose filename contains the keyword volatile is no longer cached. All other static files are still being cached on the server side.

My checks seem to indicate that this is now working. Again, anyone with more background knowledge please comment, improve on this, or correct me.

1 Like

A little bit loosely related, but if you want to load in files dynamically with renaming them (for example client side JS/css files, etc), here’s a workaround. Instead of adding your scripts directly to the resources in HA, just add a JS file that will load your files into the frontend with a unique identifier added to it as a query parameter.

I created a JS file called scripts.js and uploaded it. I then added it to my resources.

(() => {
    [
        '/local/myfile.js',
    ].forEach(path => {
        var script = document.createElement('script');
        var uuid = (() => {
            'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
                var r = Math.random() * 16 | 0;
                var v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        })();
        script.setAttribute('src', `${path}?uuid=${uuid}`);
        script.setAttribute('type', 'module');
        document.head.appendChild(script);
    });
})();

So basically it will loop over the array of JS paths you provide, add a UUID to it as a query parameter, create a element for it and add it to the of the document.

After changing your file, uploading it and refreshing the page, it will bypass the cache since the url for the file is different on every page load. You could do this for every type of file you want to load into the DOM.

1 Like

@Fry1 I’m afraid your code didn’t work for me… :frowning:

I think you have a else where it should be else:

But even with that, the UI doesn’t render when my file has that change…

Do you know where a js file is cached by HASS? I wish I could just delete my cached file and get on with my developments.

I am trying to make changes in a file called
http://hass:8123/hacsfiles/todoist-card/todoist-card.js
and I keep getting the old version, and I am sure it’s a server-side issue, I’ve tried different browsers loading only that resource separately. I always get the old file, not the new one…

I guess you copy pasted the solution but look this line

if 'volatile' not in str(filename) and (filepath := PATH_CACHE.get(key)) is None:

It means that only file having “volatile” in their names will be affected by its code

That code didn’t work for me in any case, no matter what the file name.

I’ve since discovered why, it’s because my case was different, a bit more peculiar (involving js files in HACS-installed cards).

I documented my findings here:

Hi all!
This actually works great but had to change one line because the uuid wasn’t generated and only read “undefined”.

(() => {
    [
        '/local/horseshoe/flex-horseshoe-card.js',
    ].forEach(path => {
        var script = document.createElement('script');
        var uuid = (() => {
            return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
                var r = Math.random() * 16 | 0;
                var v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
        })();
        script.setAttribute('src', `${path}?uuid=${uuid}`);
        script.setAttribute('type', 'module');
        document.head.appendChild(script);
    });
})();

Also, /local/ reffers to the /www directory.
Also, you have to not already reference the file either in references or loaded by for example HACS.

I am so happy to have found this because this makes everything so much easier !!!

/Andrei