How to determine actual /config path in Python

The python program Predbat can be installed in different ways, either via AppDaemon, via a bundle of AppDaemon and Predbat or via a custom add-on.

Each installation method has a different slug name.

When Predbat runs, HA provides Predbat with access to /config which is actually a symbolic link to /addon_configs/[slug number_add-on name]

So when Predbat writes to /config/predbat.json, it is actually being written to /addon_configs/46f69597_appdaemon/predbat.json (if running under AppDaemon for example, or a different number & name if using one of the other installation methods).

The problem is that in the debug log, if I say “saved in /config/predbat.json”, this isn’t the case the file isn’t in /config at all and its confusing to users.

How can I find the actual pathname that /config is symbolically linked to, or find the slug name/number from the running Python code so that the debug log can give a meaningful pathname? I have looked through the documentation and the community forum but can’t seem to find a way to determine this.

Original Predbat issue

Read the symlink of config

Python os.readlink() Method.

Brilliant, thank you, that looks to be just what I needed.

Spent ages searching but couldn’t find the answer

Have followed the tutorial code, and also tried other sites to double check how to call os.readlink, but it fails :frowning:

Code fragment;

        self.config_root = "/config"
        try: 
        # if config_root is a symbolic link, find the true pathname (e.g. /config ==> /addon_configs/[addon slug id & name]
            src = ""
            self.log("config_root={}".format(self.config_root))
            
            src = os.readlink(self.config_root)
            self.log("src2={}".format(src))

            self.config_root = src
            self.log("Config root is now {}".format(self.config_root))
        except OSError as exp:
            self.log("Config root unchanged at {}, error {}".format(self.config_root, exp))

I get;

Config root is /config
src1=
config_root=/config
Config root unchanged at /config error [Errno 22] Invalid argument: ‘/config’

Tried it with a full pathname, e.g. /config/predbat.json and get Errno 22 as well.

According to stack overflow, errno 22 indicates that the path (/config) is not a symbolic link, but yet writing to /config/predbat.json results in the file actually being written to /addon_configs/[add on slug]/predbat.json - so logically it must be a symbolic link? :man_shrugging:

Not an expert on addons but wondering if this is a docker volume mapped to config inside the container and not a symlink?

That was exactly what I was thinking too, it’s docker that is presenting the /config volume not the OS. And that would make sense, if it was done in the OS as a symlink then all the add-on’s would see the same /config directory whereas each add-on sees the directory mapped differently.

Seems there are potentially different ways of doing this in docker: volumes or bind mounts.

Am not particularly familiar with docker but looking at the appdaemon add-on code I can’t see anything that determines and mounts /config to put me on the trail of how to then work out what its mapped to

It is the supervisor that handles this (as that controls the addons).

Here is where it is happening but not had time to look through fully to see how you get access to that (doesn’t seem available in supervisor api), so maybe you would have to have access to the docker api and find it that way.

Thank you. It looks like that’s the appropriate piece of code that does the bind mounts for the different volumes as seen by the add-on.

But then how to get that information out of docker/the supervisor?

I’ve looked at the supervisor API calls Endpoints | Home Assistant Developer Docs and the mount API calls only give details of all mounts known to the supervisor, not to the specifics per add-on.

Looking at the add-on API calls Endpoints | Home Assistant Developer Docs there doesn’t appear to be one that lists the bind mounts either.

But if I get the token from the SUPERVISOR_TOKEN environment variable and call the API /addons/self/info I can at least get the slug name and from there derive the true /config path

Unless anyone has any better ideas?

I got this working using the method outlined above.

Here in brief is the code:

config_root_p = "/config" # default pathname if we can't find the slug from HA

# URL and security key for HA
ha_url = "http://supervisor/addons/self/info"
ha_key = os.environ.get("SUPERVISOR_TOKEN", None)
headers = {
    "Authorization": "Bearer " + ha_key,
    "Content-Type": "application/json",
    "Accept": "application/json",
}
res = requests.post(ha_url, headers=headers, timeout=TIMEOUT)

if res:
    # get add-on slug name which is the actual directory name under /addon_configs that /config is mounted to
    # and use slug name to determine printable config_root pathname when writing debug info to the log file
    config_root_p = "/addon_configs/" + res["data"]["slug"]