Home Assistant starten op Android: Tailscale VPN aanzetten en de HA-app openen in één tik

· 3 min lezen

Home Assistant draait bij mij thuis, en ik wil ’m ook onderweg kunnen bedienen. Maar mijn HA-server is alleen bereikbaar via zijn Tailscale MagicDNS-naam (https://homeassistant.<tailnet>.ts.net), en die naam wordt alleen opgelost als de VPN-tunnel actief is. Thuis én onderweg geldt dus hetzelfde: eerst Tailscale aan, dan pas de HA-app. Precies daar wilde ik een betrouwbare oplossing voor.

Waarom niet gewoon Tasker?

Eerst dacht ik logisch na: ik heb toch Tasker op mijn Samsung A26. Maar Tasker Tailscale laten bedienen bleek een wirwar van randgevallen:

  • De broadcast-intent werkt wel, maar de “App in voorkant”-detectie vereist dat de Tasker accessibility-service aanstaat.
  • Ik struikelde over package visibility: een app mag de launcher-activity van een andere app niet zomaar “zien” zonder <queries>-declaratie.
  • Exit-taken met een extra conditie worden alleen geëvalueerd op het moment dat het profiel deactiveert — dus “uit bij scherm-uit” werkte niet als je HA al verlaten had terwijl het scherm nog aan was.
  • Een community-profiel importeren van TaskerNet dekt bovendien alleen de connect, niet de disconnect.

Kortom: veel fragiele bouwstenen voor iets simpels. Dus heb ik de omgekeerde weg gekozen: een eigen, minimale app die precies twee dingen doet.

De oplossing: een mini-app met twee knoppen

Tailscale Android stelt officiële broadcast intents beschikbaar om de VPN programmatisch te bedienen:

ActieIntent
VPN aancom.tailscale.ipn.CONNECT_VPN
VPN uitcom.tailscale.ipn.DISCONNECT_VPN

Beide gericht op de receiver com.tailscale.ipn/.IPNReceiver. Een app kan die gewoon sturen — precies hetzelfde mechanisme als wat Tasker intern deed, maar zonder alle tussenlaag.

De app die ik ervoor bouwde heeft dan ook maar twee knoppen, bewust ver uit elkaar gezet om fouttappen te voorkomen:

  • VPN aan + HA openen — stuurt CONNECT_VPN, wacht ~3 seconden (Tailscale-handshake), en opent dan de Home Assistant app.
  • VPN uit — stuurt DISCONNECT_VPN en sluit zichzelf.

Opvallendste hobbels tijdens het bouwen

Een paar dingen die ik tegenkwam en de moeite waard zijn om te onthouden:

  1. getLaunchIntentForPackage retourneerde null terwijl de HA-app gewoon geïnstalleerd was. Android 11+ package-visibility: de app mocht de HA-launcher niet zien. Oplossing: een <queries>-blok in het manifest met een MAIN/LAUNCHER intent-filter — daarmee wordt de launcher zichtbaar. Dit is een bekende valkuil voor elke app die een andere app moet openen.

  2. Classes mislukten in d8: de oudere build-tools-d8 gaf een NoClassDefFoundError op de anonieme inner classes, omdat die aparte .class-bestanden niet mee-dext werden. Twee fixes hoorden erbij: alle *.class-bestanden doorgeven, én een nieuwere d8 gebruiken die overweg kan met de gegenereerde inner classes.

  3. De hostnaam is niet je LAN-IP. Toen ik de adreslijst checkte via ADB bleek 192.168.5.200 de telefoon te zijn, niet HA. Met een tailnet-naam als server-adres maakt dat gelukkig niet uit — zolang de tunnel maar omhoog is.

Gradle-vrij bouwen

De app is bewust zonder Gradle opgezet: een handmatige pipeline met de Android command-line tools — aapt2 voor resources, javac/d8 voor de code, zipalign + apksigner om te signen. Het hele bouwproces staat in de README en is op bijna elke machine met een Android SDK reproduceerbaar. Het resultaat is een APK van nog geen 13 kB.

Het launcher-icoon is op maat gemaakt: HA-blauw met een dikke “L” (voor Launcher), gegenereerd uit een 512px-master en gedownscaled naar alle density’s.

Openbaar op Forgejo

De code staat openbaar op mijn Forgejo, compleet met bouw- en installatie-instructies:

https://git.eddydevink.nl/eddy/tailscale-ha-launcher

Het project is gebouwd met DeepSeek V4 Flash als ontwikkelondersteuning — net als deze blog. Fouttappen hoort er niet meer bij: één tik en HA staat open, met de tunnel waar nodig.

Wil je de app zelf bouwen of aanpassen? De volledige pipeline en de knoppen (WAIT_FOR_VPN_MS, HA_PKG) staan in de README aan boord.