Homeassistant core on android

Hello everyone got home assistance core running on termux proot but with lots of issues, nevertheless if you want to try here are the steps

1- update termux

yes | apt update && apt upgrade

2- install proot

apt install proot
apt install proot-distro

3- install ubuntu

proot-distro install ubuntu
proot-distro login ubuntu
4- update ubuntu packages

apt update && apt upgrade -y

5- installation of dependency

apt install python3 python3-pip python3-venv python3-dev build-essential libffi-dev libssl-dev libjpeg-dev zlib1g-dev

6-create a virtual environment

python3 -m venv homeassistant

7-activate the virtual environment

source homeassistant/bin/activate

8-install home assistance core

pip install homeassistant
(It will take a while)

9-start home assistance

hass

Open up your browser and navigate to http://localhost:8123
I tasted on galaxy note9 (exynos)
I am not good in linux,if you know better please try to improve the installation method and help out others and me as well😊
Thank you

3 Likes

Take a look here:

Trust me that if you follow that guide that will give pain more then you can handle :slightly_smiling_face:
I think you didn’t have tried the guide that you replied with
Thank you

1 Like


i try, but it stop in here

Still no success, but anyway this method looks much tidier and easier than two others I tried before.
Three additional steps I had to add:

7.1. pip install --upgrade pip
7.2. apt install cargo   #(additional 400MB!)
7.3. apt install pkg-config

The error I get is:

[homeassistant.util.package] Unable to install package PyQRCode==1.2.1: error: No virtual environment found; run `uv venv` to create an environment, or pass `--system` to install into a non-virtual environment

Actually there is lots of those - not only PyQRCode. But they say essentially the same.

Probably just some variables or paths needed to set, but I’m not familiar with python and venv.

Work on my old android device , original rom and not root. (19.12.24).
Termux F-Driod version

termux-tools version: 1.44.1
Android version: 8.1.0
Kernel build information: Linux localhost 4.4.78-perf+ #1 SMP PREEMPT Sat Jul 7 00:12:00 CST 2018 aarch64 Android
Device manufacturer: Xiaomi
Device model: MI PAD 4
HA Core : 2004.11.3

step 5 Install the dependencies chang to : https://www.home-assistant.io/installation/linux/

root@localhost:~# apt install python3 python3-dev python3-venv python3-pip bluez libffi-dev libssl-dev libjpeg-dev zlib1g-dev
root@localhost:~# apt install autoconf build-essential libopenjp2-7 libtiff6 libturbojpeg0-dev tzdata ffmpeg liblapack3 liblapack-dev libatlas-base-dev

Can install and run hass.



HA-InvalidConfig

I was finally able to get HASS working on my rooted Mi 11. Installation notes here.

1 Like

the same as it, it stuck

Great work, @ondyn - though, after reviewing your code changes, I came to an “ugly” but interesting alternitve: what if we just replace uv with a wrapper script - hehe? So I asked ChatGPT to build one - well, it took longer than expected - stupid A.I., but here it is and it works great. I also had IPv6 issues on zeroconf with failing weather service and others, too…

precedence ::ffff:0:0/96  100

in /etc/gai.conf didn’t work either, so I patched lib/python3.13/site-packages/zeroconf/_utils/net.py in ip6_addresses_to_indexes to swallow wired, failing adapters from

    for iface in interfaces:
        if isinstance(iface, int):
            result.append((interface_index_to_ip6_address(adapters, iface), iface))  # type: ignore[arg-type]
        elif isinstance(iface, str) and ipaddress.ip_address(iface).version == 6:
            result.append(ip6_to_address_and_index(adapters, iface))  # type: ignore[arg-type]

to

    for iface in interfaces:
        try:
            if isinstance(iface, int):
                result.append((interface_index_to_ip6_address(adapters, iface), iface))  # type: ignore[arg-type]
            elif isinstance(iface, str) and ipaddress.ip_address(iface).version == 6:
                result.append(ip6_to_address_and_index(adapters, iface))  # type: ignore[arg-type]
        except:
            pass

Anyway, latest, untouched HA-Core is now running fine, when I install homeassistant and before first launch call this vu_wrapper.sh to direct all uv pip things to pip when HA is trying to install things on first launch :slight_smile:

uv_wrapper.sh:

#!/bin/sh
#
# A single POSIX-compatible script that:
#  1) On "install": replaces the system uv with this wrapper
#  2) On "uninstall": restores the original uv
#  3) Otherwise acts as the uv wrapper itself

# Current script path
MANAGER_SCRIPT="$0"

# Try to find the "real" uv in PATH
SYSTEM_UV="$(command -v uv 2>/dev/null || true)"

show_usage() {
  echo "Usage:"
  echo "  uv install       → replace the system uv with this wrapper"
  echo "  uv uninstall     → restore the original uv"
  echo "  uv pip [...]     → call pip"
  echo "  uv venv [...]    → call python3 -m venv"
  echo "  uv install ...   → same as pip install ..."
  echo "  uv uninstall ... → same as pip uninstall ..."
  echo "  uv [other]       → forward to original uv_disabled"
  exit 0
}

# ----------------------------
# 1) Handle "install"
# ----------------------------
if [ "$1" = "install" ]; then
  # Check if the wrapper is already installed (presence of uv_disabled indicates an active wrapper)
  if [ -n "$SYSTEM_UV" ] && [ -f "${SYSTEM_UV}_disabled" ]; then
    echo "⚠️  It appears the wrapper is already installed at $SYSTEM_UV"
    echo "    Original binary: ${SYSTEM_UV}_disabled"
    exit 1
  fi

  if [ -z "$SYSTEM_UV" ]; then
    echo "❌ Error: No 'uv' found in PATH. Cannot install wrapper."
    exit 1
  fi

  # If the discovered uv equals this script, we're already running as uv.
  # Possibly the user manually placed this script at /usr/local/bin/uv, etc.
  # => If so, either it's already installed or we can't reinstall
  if [ "$SYSTEM_UV" = "$MANAGER_SCRIPT" ]; then
    echo "⚠️  This script is already located at $SYSTEM_UV."
    echo "    If you intended to install, place it elsewhere and run './uv_manager.sh install'."
    exit 1
  fi

  # Optional check: if uv is a symlink
  if [ -L "$SYSTEM_UV" ]; then
    echo "⚠️  Refusing to overwrite a symlink: $SYSTEM_UV."
    echo "    Please replace the real binary manually if needed."
    exit 1
  fi

  # Rename the discovered uv to uv_disabled
  echo "🔧 Installing wrapper: renaming $SYSTEM_UV → ${SYSTEM_UV}_disabled"
  mv "$SYSTEM_UV" "${SYSTEM_UV}_disabled" 2>/dev/null || {
    echo "❌ Could not rename $SYSTEM_UV. Try running with sudo?"
    exit 1
  }

  echo "🔧 Copying this script to $SYSTEM_UV..."
  cp "$MANAGER_SCRIPT" "$SYSTEM_UV" || {
    echo "❌ Could not copy wrapper to $SYSTEM_UV. Check permissions."
    mv "${SYSTEM_UV}_disabled" "$SYSTEM_UV" 2>/dev/null || true
    exit 1
  }
  chmod +x "$SYSTEM_UV"
  echo "✅ Wrapper installed successfully!"
  echo "   Original uv binary saved as: ${SYSTEM_UV}_disabled"
  exit 0
fi

# ----------------------------
# 2) Handle "uninstall"
# ----------------------------
if [ "$1" = "uninstall" ]; then
  if [ -z "$SYSTEM_UV" ] || [ ! -f "${SYSTEM_UV}_disabled" ]; then
    echo "⚠️  The wrapper does not seem to be installed (no uv_disabled found)."
    exit 1
  fi

  # Check if we really are the wrapper script
  if [ "$SYSTEM_UV" != "$MANAGER_SCRIPT" ]; then
    echo "⚠️  The uv in PATH ($SYSTEM_UV) is not the same as this script ($MANAGER_SCRIPT)."
    echo "    Possibly the wrapper is installed elsewhere, or the system is inconsistent."
    exit 1
  fi

  echo "🔁 Restoring original uv from ${SYSTEM_UV}_disabled → $SYSTEM_UV"
  rm -f "$SYSTEM_UV" || {
    echo "❌ Could not remove wrapper at $SYSTEM_UV."
    exit 1
  }
  mv "${SYSTEM_UV}_disabled" "$SYSTEM_UV" || {
    echo "❌ Could not restore original uv."
    exit 1
  }
  chmod +x "$SYSTEM_UV"
  echo "✅ Original uv restored."
  exit 0
fi

# ----------------------------
# 3) On any other call with arguments,
#    we assume we're running in "wrapper mode"
# ----------------------------

# If user calls "uv" with no arguments → show usage
if [ $# -eq 0 ]; then
  show_usage
fi

UV_DISABLED="${MANAGER_SCRIPT}_disabled"

# (a) uv pip ...
if [ "$1" = "pip" ]; then
  shift
  ARGS=""
  SKIP_NEXT=0
  for arg in "$@"; do
    if [ "$SKIP_NEXT" -eq 1 ]; then
      SKIP_NEXT=0
      continue
    fi
    if [ "$arg" = "--index-strategy" ]; then
      SKIP_NEXT=1
      continue
    fi
    ARGS="$ARGS \"$arg\""
  done
  # shellcheck disable=SC2086
  eval exec pip $ARGS

# (b) uv venv ...
elif [ "$1" = "venv" ]; then
  shift
  if [ -z "$1" ]; then
    exec python3 -m venv .venv
  else
    exec python3 -m venv "$@"
  fi

# (c) uv install => pip install ...
elif [ "$1" = "install" ]; then
  shift
  exec pip install "$@"

# (d) uv uninstall => pip uninstall ...
elif [ "$1" = "uninstall" ]; then
  shift
  exec pip uninstall "$@"

# (e) All other commands => forward to uv_disabled
else
  if [ -x "$UV_DISABLED" ]; then
    exec "$UV_DISABLED" "$@"
  else
    echo "❌ Original uv not found at: $UV_DISABLED"
    echo "   Possibly it's already uninstalled or the system is inconsistent."
    exit 1
  fi
fi

My final installation steps on an old Asus ZenFone 4 (ZE554KL) with stock un-rooted Android 8 is:

  1. Install F-Droid from homepage
  2. In F-Droid install Termux and Termux:Boot
  3. Launch Termux:Boot and then Termux and enter commands:
pkg update
pkg upgrade -y
pkg install openssh -y
passwd
sshd
# continue in ssh

pkg install proot-distro -y
proot-distro install debian
proot-distro login debian
apt update && apt upgrade -y

# follow https://github.com/pascallj/python3.13-backport to install python 3.13
apt install wget -y && wget -qO- https://pascalroeleven.nl/deb-pascalroeleven.gpg > /etc/apt/keyrings/deb-pascalroeleven.gpg

cat <<EOF | tee /etc/apt/sources.list.d/pascalroeleven.sources
Types: deb
URIs: http://deb.pascalroeleven.nl/python3.13
Suites: bookworm-backports
Components: main
Signed-By: /etc/apt/keyrings/deb-pascalroeleven.gpg
EOF

apt update && apt install python3.13 python3.13-dev python3.13-venv build-essential ffmpeg libisal-dev libturbojpeg0 libpcap0.8 -y

# install HA-Core
python3.13 -m venv hass
source hass/bin/activate
pip install --upgrade pip wheel
pip install homeassistant

# patch lib/python3.13/site-packages/zeroconf/_utils/net.py in ip6_addresses_to_indexes to fix USB, DHPC and zeroconf errors
python -c "import zeroconf._utils.net as m; p=m.__file__; o=open(p).read(); old=('    for iface in interfaces:\\n        if isinstance(iface, int):\\n            result.append((interface_index_to_ip6_address(adapters, iface), iface))  # type: ignore[arg-type]\\n        elif isinstance(iface, str) and ipaddress.ip_address(iface).version == 6:\\n            result.append(ip6_to_address_and_index(adapters, iface))  # type: ignore[arg-type]'); new=('    for iface in interfaces:\\n        try:\\n            if isinstance(iface, int):\\n                result.append((interface_index_to_ip6_address(adapters, iface), iface))  # type: ignore[arg-type]\\n            elif isinstance(iface, str) and ipaddress.ip_address(iface).version == 6:\\n                result.append(ip6_to_address_and_index(adapters, iface))  # type: ignore[arg-type]\\n        except:\\n            pass'); patched=o.replace(old,new); import sys; open(p,'w').write(patched); print('No changes made. Possibly already patched or code differs:', p) if patched==o else print('Successfully patched:', p)"

# run HA
hass -v

# wired thing was, now I didn't need the vu_wrapper.sh script anymore, updates to packages or the installation order might have fixed it - haha :-)

# Finally, create boot scripts
apt install daemonize -y
mkdir -p ~/.termux/boot
echo '#!/data/data/com.termux/files/usr/bin/bash
  termux-wake-lock
  sshd
' > ~/.termux/boot/01-sshd.sh && chmod +x ~/.termux/boot/01-sshd.sh
echo '#!/data/data/com.termux/files/usr/bin/bash
daemonize $(which proot-distro) login debian -- bash -c "
  cd ~
  source hass/bin/activate
  while true; do  
    hass
  done
"' > ~/.termux/boot/02-hass.sh && chmod +x ~/.termux/boot/02-hass.sh