[Windows] Setting up Activitywatch and SSH tunnel to be started via scheduled powershell script

Hi !

I wanted something that would create an SSH tunnel on Windows to my Activitywatch remote server and then would start Activitywatch so it would connect to that remote server.

I’ve initially attempted to do this with Windows Services, but failed because aw-watcher-window would not run as a Windows Service (had some OLE Errors in the logs).

Eventually I settled for writing a Powershell script that does what I needed and thought I’d share this here:

<#

Pre-requisites:

The built-in Windows SSH client needs to be installed:

    Add-WindowsCapability -Online -Name OpenSSH.Client*

Description:

Make sure to adapt the script to your setup:
- the private key
- the paths
- the IP for the remote server
- the windows user
- the user on the remote server

If we have network connectivity, we do the following:
1. Start the WSL Debian SSH daemon
2. Start the SSH tunnel to the Activitywatch Remote Server
3. Start ActivityWatch (aw-server, aw-watcher-window, aw-watcher-afk)

Note: The first step above is optional. I have it here because I find it convenient to have an SSHD running on the machine.

Installing this script to run at Windows10 logon:

    schtasks /TN AWStartup /Create /TR "powershell.exe -file c:\users\user\aw\aw-local\tunnel_on_startup.ps1" /RU user /SC ONLOGON /IT

Uninstall:

    schtasks.exe /delete /tn AWStartup /f
#>


function testport{
  param([string]$hostname,[int]$port,[int]$timeout) 

  $requestCallback = $state = $null
  $client = New-Object System.Net.Sockets.TcpClient
  
  $where = [IPAddress]$hostname.ToString()
  $beginConnect = $client.BeginConnect($where,$port,$requestCallback,$state)
  Start-Sleep -milli $timeOut
  if ($client.Connected) { $open = $true } else { $open = $false }
  $client.Close()
  [pscustomobject]@{hostname=$hostname;port=$port;open=$open}
}

$max_retries = 7

$has_network_connectivity = $false;
for($i=0;$i -lt $max_retries;$i++){
    $ping = testport -hostname "192.168.1.100" -port 2223 -timeout 800
    if($ping.open) {
        $has_network_connectivity = $true
        break
    }
}

if(! $has_network_connectivity) {
    Write-Host "Not connected!"
} else {
    Write-Host "Connected!"
    bash -c "sudo service ssh --full-restart"
    Start-Process "C:\Windows\System32\OpenSSH\ssh.exe" "-p 2223 -i c:\users\user\aw\aw-local\aw3.private -N -L 5600:127.0.0.1:5602 [email protected]"
    Sleep 1
    Start-Process "C:\Users\user\aw\aw-local\activitywatch\aw-qt.exe"
}


Write-Host "Finished.."
Write-Host "Sleeping 10 seconds .."
Sleep 10

In the above, the SSH tunnel (for the remote server) is started on purpose before aw-qt (which as of writing this, starts a local aw-server as well) with the intention to have the watchers send to the remote server instead of the local aw-server. This works for me, but… ideally, on Windows, whichever second app tries to listen on the same exact port (in this case either the tunnel or the local aw-server) should fail (this is known not to be the case on Windows, if anyone has more information on this please write a note about it).

Something I liked about Activitywatch builds is they come in both .zip and .exe with an installer, and in my case I liked to use the .zip because I wanted to customize the startup as mentioned above.