Using Tasker to establish a WireGuard tunnel before starting the Home Assistant Companion app

Updated on 9/21.

A downloadable multi app version of this task has been uploaded to Taskernet.com. Search for “WireGuard Auto Connect.”

If you rely on WireGuard for occasional remote access, don’t want to run the app all the time when you aren’t at home, and don’t access HA remotely often enough to make HA Cloud worthwhile, this task will make it much more convenient to access your Home Assistant server when you’re away. This is a Task, rather than a Profile because Tasker has difficulty reliably detecting via profile when an app is running, probably due to Android limitations.

This task has two modes:

Local mode: If your phone is connected to your home network Home Assistant Companion starts normally and the task exits.

Remote mode: If your phone is not connected to your home network, this task activates a WireGuard tunnel, verifies the HA server is accessible and then starts HA Companion. When HA Companion is no longer in the foreground the WG tunnel is shut down and the task exits to the home screen.

What’s needed:

  • An Android phone. (Tested on an unrooted Oneplus 9Pro running Android 13.)
  • Your phone’s static IP address on your home network, or if you prefer to leave Location enabled on your phone, your WiFi SSID.
  • Tasker
  • A working Wireguard server on your home network. (I’m using PI VPN running on the same server as Home Assistant.)
  • The Wireguard app on your phone with a working tunnel configured that allows access to your HA server.
  • Your HA server configured to respond to pings.

Setup:

  1. Copy the code below to a text file and name it “Home_Assistant.tsk.xml”.
  2. Import the Home_Assistant.tsk.xml file into Tasker by long clicking on the Tasks tab at the top.
  3. Configure the variables in the variable entry section.
  4. If desired, change the Icon by tapping the dots at the bottom center of the screen.
  5. Turn on “Allow remote control apps” in Wireguard Settings / Advanced.
  6. In Tasker Permissions enable “Control Wireguard Tunnels.”
  7. In the main Tasker screen tap the 3 dot menu. Select “More” and “Android Settings”, then turn on “App Usage Stats” for Tasker.
  8. Create a widget for the task on your launcher. You should no longer need a separate HA Companion app icon.

Note: After connecting through WireGuard and leaving HA Companion, this script is not able to reestablish the WG tunnel if you switch back to the HA Companion app. HA Companion must be restarted from the widget to reestablish the tunnel.

<TaskerData sr="" dvi="1" tv="6.2.12-rc">
	<Task sr="task72">
		<cdate>1690430702327</cdate>
		<edate>1693765479317</edate>
		<id>72</id>
		<nme>Home Assistant</nme>
		<pri>100</pri>
		<showinnot>false</showinnot>
		<Kid sr="Kid">
			<launchID>65</launchID>
			<pkg>com.bluescloud.www.ha</pkg>
			<vTarg>29</vTarg>
			<vnme>1.0</vnme>
			<vnum>2</vnum>
		</Kid>
		<Action sr="act0" ve="7">
			<code>300</code>
			<label>Variable entry</label>
		</Action>
		<Action sr="act1" ve="7">
			<code>300</code>
			<label>Home Assistant IP</label>
		</Action>
		<Action sr="act10" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%PingTries</Str>
			<Str sr="arg1" ve="3">3</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act11" ve="7">
			<code>300</code>
			<label>End of variable entry</label>
		</Action>
		<Action sr="act12" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%Retrycount</Str>
			<Str sr="arg1" ve="3">%PingTries</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="1"/>
			<Int sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act13" ve="7">
			<code>37</code>
			<ConditionList sr="if">
				<bool0>Or</bool0>
				<Condition sr="c0" ve="3">
					<lhs>%WIFII</lhs>
					<op>4</op>
					<rhs>%IP1orSSID1</rhs>
				</Condition>
				<Condition sr="c1" ve="3">
					<lhs>%WIFII</lhs>
					<op>4</op>
					<rhs>%IP2orSSID2</rhs>
				</Condition>
			</ConditionList>
		</Action>
		<Action sr="act14" ve="7">
			<code>20</code>
			<App sr="arg0">
				<appClass>io.homeassistant.companion.android.launch.LaunchActivity</appClass>
				<appPkg>io.homeassistant.companion.android</appPkg>
				<label>Home Assistant</label>
			</App>
			<Str sr="arg1" ve="3"/>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
		</Action>
		<Action sr="act15" ve="7">
			<code>135</code>
			<Int sr="arg0" val="1"/>
			<Int sr="arg1" val="1"/>
			<Str sr="arg2" ve="3">End</Str>
		</Action>
		<Action sr="act16" ve="7">
			<code>38</code>
		</Action>
		<Action sr="act17" ve="7">
			<code>20</code>
			<App sr="arg0">
				<appClass>com.wireguard.android.activity.MainActivity</appClass>
				<appPkg>com.wireguard.android</appPkg>
				<label>WireGuard</label>
			</App>
			<Str sr="arg1" ve="3"/>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
		</Action>
		<Action sr="act18" ve="7">
			<code>365</code>
			<Bundle sr="arg0">
				<Vals sr="val">
					<net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""/&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
					<net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
				</Vals>
			</Bundle>
			<Str sr="arg1" ve="3">WireGuardSetTunnel(true,%WGTunnel)</Str>
		</Action>
		<Action sr="act19" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%Loopcount</Str>
			<Str sr="arg1" ve="3">0</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act2" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%HA_IP</Str>
			<Str sr="arg1" ve="3">Your HS server's private IP</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act20" ve="7">
			<code>30</code>
			<Int sr="arg0" val="0"/>
			<Int sr="arg1" val="1"/>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
		</Action>
		<Action sr="act21" ve="7">
			<code>300</code>
			<label>WaitingWG</label>
		</Action>
		<Action sr="act22" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%Loopcount</Str>
			<Str sr="arg1" ve="3">%Loopcount+1</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="1"/>
			<Int sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act23" ve="7">
			<code>37</code>
			<ConditionList sr="if">
				<Condition sr="c0" ve="3">
					<lhs>%Loopcount</lhs>
					<op>0</op>
					<rhs>2</rhs>
				</Condition>
			</ConditionList>
		</Action>
		<Action sr="act24" ve="7">
			<code>550</code>
			<Str sr="arg0" ve="3"/>
			<Str sr="arg1" ve="3">Waiting for Home Assistant</Str>
			<Str sr="arg2" ve="3"/>
			<Str sr="arg3" ve="3">Popup</Str>
			<Int sr="arg4" val="2"/>
			<Int sr="arg5" val="1"/>
		</Action>
		<Action sr="act25" ve="7">
			<code>38</code>
		</Action>
		<Action sr="act26" ve="7">
			<code>37</code>
			<ConditionList sr="if">
				<Condition sr="c0" ve="3">
					<lhs>%Loopcount</lhs>
					<op>0</op>
					<rhs>%PingTries</rhs>
				</Condition>
			</ConditionList>
		</Action>
		<Action sr="act27" ve="7">
			<code>550</code>
			<Str sr="arg0" ve="3"/>
			<Str sr="arg1" ve="3">No Home Assistant Response - Exiting</Str>
			<Str sr="arg2" ve="3"/>
			<Str sr="arg3" ve="3">Popup</Str>
			<Int sr="arg4" val="5"/>
			<Int sr="arg5" val="1"/>
		</Action>
		<Action sr="act28" ve="7">
			<code>135</code>
			<Int sr="arg0" val="1"/>
			<Int sr="arg1" val="1"/>
			<Str sr="arg2" ve="3">WGOff</Str>
		</Action>
		<Action sr="act29" ve="7">
			<code>38</code>
		</Action>
		<Action sr="act3" ve="7">
			<code>300</code>
			<label>Network 1 SSID or Phone IP (Location must be on to use SSID)</label>
		</Action>
		<Action sr="act30" ve="7">
			<code>320</code>
			<se>false</se>
			<Str sr="arg0" ve="3">%HA_IP</Str>
			<Int sr="arg1" val="1"/>
			<Str sr="arg2" ve="3"/>
			<Str sr="arg3" ve="3">%Pingtime</Str>
			<Str sr="arg4" ve="3"/>
		</Action>
		<Action sr="act31" ve="7">
			<code>37</code>
			<ConditionList sr="if">
				<Condition sr="c0" ve="3">
					<lhs>%err</lhs>
					<op>0</op>
					<rhs>1</rhs>
				</Condition>
			</ConditionList>
		</Action>
		<Action sr="act32" ve="7">
			<code>135</code>
			<Int sr="arg0" val="1"/>
			<Int sr="arg1" val="1"/>
			<Str sr="arg2" ve="3">WaitingWG</Str>
		</Action>
		<Action sr="act33" ve="7">
			<code>38</code>
		</Action>
		<Action sr="act34" ve="7">
			<code>300</code>
			<label>StartHA</label>
		</Action>
		<Action sr="act35" ve="7">
			<code>20</code>
			<App sr="arg0">
				<appClass>io.homeassistant.companion.android.launch.LaunchActivity</appClass>
				<appPkg>io.homeassistant.companion.android</appPkg>
				<label>Home Assistant</label>
			</App>
			<Str sr="arg1" ve="3"/>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
		</Action>
		<Action sr="act36" ve="7">
			<code>300</code>
			<label>WaitingHA</label>
		</Action>
		<Action sr="act37" ve="7">
			<code>37</code>
			<ConditionList sr="if">
				<Condition sr="c0" ve="3">
					<lhs>%LAPP</lhs>
					<op>3</op>
					<rhs>WireGuard</rhs>
				</Condition>
			</ConditionList>
		</Action>
		<Action sr="act38" ve="7">
			<code>30</code>
			<Int sr="arg0" val="500"/>
			<Int sr="arg1" val="0"/>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
		</Action>
		<Action sr="act39" ve="7">
			<code>135</code>
			<Int sr="arg0" val="1"/>
			<Int sr="arg1" val="19"/>
			<Str sr="arg2" ve="3">WaitingHA</Str>
		</Action>
		<Action sr="act4" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%IP1orSSID1</Str>
			<Str sr="arg1" ve="3">Your phone's private static IP or your WiFi SSID.</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act40" ve="7">
			<code>38</code>
		</Action>
		<Action sr="act41" ve="7">
			<code>300</code>
			<label>HAActive</label>
		</Action>
		<Action sr="act42" ve="7">
			<code>37</code>
			<ConditionList sr="if">
				<Condition sr="c0" ve="3">
					<lhs>%LAPP</lhs>
					<op>3</op>
					<rhs>Home Assistant</rhs>
				</Condition>
			</ConditionList>
		</Action>
		<Action sr="act43" ve="7">
			<code>30</code>
			<Int sr="arg0" val="500"/>
			<Int sr="arg1" val="0"/>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
		</Action>
		<Action sr="act44" ve="7">
			<code>135</code>
			<Int sr="arg0" val="1"/>
			<Int sr="arg1" val="19"/>
			<Str sr="arg2" ve="3">HAActive</Str>
		</Action>
		<Action sr="act45" ve="7">
			<code>38</code>
		</Action>
		<Action sr="act46" ve="7">
			<code>300</code>
			<label>WGOff</label>
		</Action>
		<Action sr="act47" ve="7">
			<code>365</code>
			<Bundle sr="arg0">
				<Vals sr="val">
					<net.dinglisch.android.tasker.RELEVANT_VARIABLES>&lt;StringArray sr=""/&gt;</net.dinglisch.android.tasker.RELEVANT_VARIABLES>
					<net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>
				</Vals>
			</Bundle>
			<Str sr="arg1" ve="3">WireGuardSetTunnel(false,%WGTunnel)</Str>
		</Action>
		<Action sr="act48" ve="7">
			<code>25</code>
			<Int sr="arg0" val="0"/>
			<Str sr="arg1" ve="3"/>
		</Action>
		<Action sr="act49" ve="7">
			<code>300</code>
			<label>End</label>
		</Action>
		<Action sr="act5" ve="7">
			<code>300</code>
			<label>Network 2 SSID or Phone IP (Location must be on to use SSID)</label>
		</Action>
		<Action sr="act6" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%IP2orSSID2</Str>
			<Str sr="arg1" ve="3">Your phone's 2nd private static IP or your 2nd WiFi SSID.  Leave unchanged if you only have a single network.
</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act7" ve="7">
			<code>300</code>
			<label>WireGuard Tunnel Name</label>
		</Action>
		<Action sr="act8" ve="7">
			<code>547</code>
			<Str sr="arg0" ve="3">%WGTunnel</Str>
			<Str sr="arg1" ve="3">Your WG tunnel name</Str>
			<Int sr="arg2" val="0"/>
			<Int sr="arg3" val="0"/>
			<Int
 sr="arg4" val="0"/>
			<Int sr="arg5" val="3"/>
			<Int sr="arg6" val="1"/>
		</Action>
		<Action sr="act9" ve="7">
			<code>300</code>
			<label>Server Ping Tries (~4 seconds/ping before failing)</label>
		</Action>
		<Img sr="icn" ve="2">
			<fle>/storage/emulated/0/QuickShare/ic_launcher_round.png</fle>
		</Img>
	</Task>
</TaskerData>

Below are the individual tasks entries contained in the XML if you prefer to recreate the task from the Tasker UI. This list was transcribed manually from my phone (there doesn’t seem to be a way to export it) so there may be minor typos.

1	Label	Variable Entry Section	
2	Label	Home Assistant IP	
3	Variable Set	Name %HA_IP To <Your HA server’s private IP Address>	
4	Label	Network 1 SSID or phone IP (Location must be on to use SSID)	
5	Variable Set	Name %IP1orSSID1 To <Network 1 - Your phone’s private static IP or your WiFi SSID>	
6	Label	Network 2 SSID or phone IP (Location must be on to use SSID)	
7	Variable Set	Name %IP1orSSID2 To <Network 2 – Your phone’s private static IP or your WiFi SSID>	
8	Label 	WireGuard Tunnel Name	
9	Variable Set	Name %WGTunnel to <Your WireGuard Tunnel Name>	
10	Label	Server Ping Tries (~4 seconds/ping before failing)	
11	Variable Set	Name %PingTries To 3	
12	Label	End of variable entry	
13	Variable Set	Name %Retrycount To %PingTries	
14	If	%WIFII ~R %IP1orSSID1 | %WIFII ~R %IP2orSSID2	
15		Launch App	Package/App Name Home Assistant 
16		Goto	Type ActionLabel Label End
17	End If		
18	Launch App	Package/App Name WireGuard	
19	Tasker Function	Function WireGuardSetTunnel (True,%WGTunnel)	
20	Variable Set	Name %Loopcount To 0	
21	Wait	1 Second	
22	Label 	WaitingWG	
23	Variable Set	Name %Loopcount To %Loopcount+1	
24	If	%Loopcount eq 2	
25		Popup	Text Waiting for Home Assistant
26	End If		
27	If	%Loopcount eq %PingTries	
28		Popup 	Text No Home Assistant Response – Exiting
29		Goto 	Type Action Label Label WGOff
30	End If		
31	Ping	Host %HA_IP Number 1	
32	If	%err eq 1	
33		Goto	Type Action Label Label WaitingWG
34	End If		
35	Label	StartHA	
36	Launch App	Package/App Name  Home Assistant	
37	Label	WaitingHA	
38	If	%LAPP !~ WireGuard	
39		Wait 	500 MS
40		Goto	Type Action Label Label WaitingHA
41	End If		
42	Label	HAActive	
43	If	%LAPP !~ Home Assistant	
44		Wait 	500 MS
45		Goto	Type Action Label Label HAActive
46	End If		
47	Label	WGOff	
48	Tasker Function	WireGuardSetTunnel (False,%WGTunnel)	
49	Go Home	Page 0	
50	Label	End	


I have no way to test this on other versions of Android, but if you have a problem getting it to work let me know and I’ll try to help.

I do similar just not as complicated. I use a Wireguard Split Tunnel so that I can access Hubitat hubs, Home Assistant, Node Red, MQTT Broker, Plex server, etc. when I am not connected to home network. I don’t open and close the tunnel prior to opening any of these apps though. It just reacts to connecting/disconnecting to/from home network.

I also have a full tunnel setup when connected to insecure Wi-Fi but rarely use that.

It all works great.

I considered that but it has the disadvantage of reduced battery life when Wireguard is running all the time, especially when I only need remote access to Home Assistant occasionally.

Hi @Stephenn

Although technically not strictly HA-related, care to share how you implemented this? I tried to set up a similar approach, but my Tasker fails to start the Wireguard tunnel when disconnected from the home WiFi. I get this error:

Not allowed to start service Intent { cmp=com.wireguard.android/.backend.GoBackend$VpnService }: app is in background

Automatic stopping of the tunnel when the phone connects to the home WiFi works like a charm though :rofl:

OnePlus 7T, OxygenOS 12.1 (Android 12), latest Tasker and Wireguard apps, both not optimised for battery.

Thanks!
Evripidis

I never ran into the error you’re receiving and think the steps required to get this working are all listed above, but since I worked on this for a couple of weeks I could have missed something. Hopefully someone will give it a try and provide feedback here. The only thing I can think of that might cause a problem are the settings in step 5 and 6.

Everything was done via the Tasker GUI and the 50 or so commands are listed in that GUI when you import it. The code above is exactly what Tasker generates when exporting a task to an XML file and thankfully goes away when the file is imported. There is no Java or other programming involved, nor are there any additional Tasker modules or addons needed.

Please let us know if you fix the problem and what caused it.

I did run across another problem when trying to get this working. The WireGuardSetTunnel command would not work consistently unless the WireGuard app was first started using the Launch App command. From the wording of the error you’re getting that may be the problem you’re running into.

I posted the tasks list from the Tasker UI. It’s much easier to follow than the XML.

Thanks for posting the task. Gives a lot of food for thought and ideas :slight_smile:

I found a workaround for my simple case. Now it works fine:

  1. (after disconnected from the home WiFi) Wait for 30 seconds
  2. Launch the WireGuard app
  3. Open the tunnel to the home VPN server
  4. Press the Back Button (so that the WireGuard app from step #2 goes away)

That’s unfortunate. My task simply starts the tunnel without starting app first. I wonder why I got lucky.

It is not working anymore on Android 15. You have to start the app, than start the tunnel. Very unfortunate. Google is killing Android with every release.

I am still on 14 and my phone is a couple years old so will knows if 15 will be an option. Do you have AutoInput installed with Tasker? It can “push” on screen buttons and is how I reboot my phone and “push” the confirmation buttons.

I’m on Pixel 7 and the update to Android 15 broke my setup. No, I don’t have AutoInput. What I did, I just start WireGuard, invoke WireGuardSetTunnel function and then simulate back button.

This still does not work if the screen is off, so if I just walk away from my home WG does not start, if I drive it work because Android Auto counts as “screen on” I suppose.

My wife’s Pixel 8 is still on Android 14 and the tasker + wireguard works flawlessly.

UPD! Found this one: GitHub - zaneschepke/wgtunnel: An alternative Android client app for WireGuard VPN

This one promises doing everything what tasker is doing, will test this one if that’s more reliable.

Wow. Great news, did you give it a go?

I want to test it in the wild, but it seems to work so far. The set up is much easier than Tasker + Wireguard Client

1 Like