Calling system binaries from AppDaemon

I have a Python script that uses Selenium WebDriver to launch a headless Chrome instance and retrieve some data from the web. On its own, the script works fine. I refactored it as an AppDaemon module and it begins to run, but fails to create the Chrome() object. It looks like this is because AppDaemon does not have the same path as my regular user. I tried adding paths in my module like this:

sys.path.append('/usr/local/bin')
sys.path.append('/usr/bin')

but it still fails with

selenium.common.exceptions.WebDriverException: Message: unknown error: Chrome failed to start: exited abnormally

probably because it’s importing the 3rd party modules before I append the paths. I thought maybe it needed a virtual display (even though the latest Chrome doesn’t) so I tried PyVirtualDisplay and xvfbwrapper. In both cases, it can’t find the Xvfb binary (which is installed in /usr/bin):

OSError: Can not find Xvfb. Please install it and try again.

So this definitely points to a path issue. Is there a way for the modules imported by my AppDaemon module to be aware of the right paths? AppDaemon is running as the “homeassistant” user on my system, and that user has the full $PATH set.

I forgot to mention I’m running AppDaemon in a Python 3 virtualenv and I’ve installed the 3rd party modules like selenium and PyVirtualDisplay in the same virtualenv. I can run the script’s commands one by one in the Python interpreter. It’s just when it runs within AppDaemon that it can’t find the binaries.

Definitely a path issue. Here’s part of the debug log.

[0.997][INFO]: Launching chrome: /opt/google/chrome/google-chrome --disable-background-networking --disable-browser-side-navigation --disable-client-side-phishing-detection --disable-default-apps --disable-gpu --disable-hang-monitor --disable-popup-blocking --disable-prompt-on-repost --disable-sync --disable-web-resources --enable-automation --enable-logging --force-fieldtrials=SiteIsolationExtensions/Control --headless --ignore-certificate-errors --load-extension=/tmp/.org.chromium.Chromium.vhbpoJ/internal --log-level=0 --metrics-recording-only --no-first-run --no-sandbox --password-store=basic --remote-debugging-port=12162 --safebrowsing-disable-auto-update --test-type=webdriver --use-mock-keychain --user-data-dir=/tmp/.org.chromium.Chromium.vlhJAY data:,
[0.998][DEBUG]: DevTools request: http://localhost:12162/json/version
/opt/google/chrome/google-chrome: line 8: readlink: command not found
/opt/google/chrome/google-chrome: line 10: dirname: command not found
/opt/google/chrome/google-chrome: line 45: exec: cat: not found
/opt/google/chrome/google-chrome: line 46: exec: cat: not found

I realize I could run this with subprocess but the goal is to process the scraped data. If I used subprocess, I’d have to pipe the output back into the script and parse it into structured data again.

adapt the py script you already have to generate the right output, or write the app so that it uses the output there is to your convienience.

most off the times i start rewriting the py code so that it is fit for use as an app.

As it turns out, /opt/google/chrome/google-chrome is a wrapper for /opt/google/chrome/chrome. I changed the module to use the actual binary instead of the wrapper, and it ran without error. Now AppDaemon will be able to read stock updates via my phone’s speaker while it brews a cup of coffee and gradually brightens the lights!

3 Likes

hi mate, I am trying to get my selenium working to fetch some bank info. do you mind sharing your python script (not appDemon). thanks in advance.