Withings-Sync Python/Powershell Script w/ Discord Webhook Notifications

I recently deployed https://github.com/jaroslawhartman/withings-sync to sync my Withings scale to my Garmin account. Here’s a quick Powershell script I whipped up that runs via Scheduled Tasks on Windows and uses a Discord Webhook to notify you of its progress:

# function adapted from https://stackoverflow.com/posts/42995301/revisions
Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
  Try {
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $commandPath
    $pinfo.RedirectStandardOutput = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $commandArguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    [pscustomobject]@{
        commandTitle = $commandTitle
        stdout = $p.StandardOutput.ReadToEnd()
    }
    $p.WaitForExit()
  }
  Catch {
     exit
  }
}

# call this with garmin username & password to sync from withings
function withings-sync( $username, $password )
{
	# setup vars 
	$hoursToPause = 2
	$fromDate = Get-Date
	$loop = $true
	$countOfTries = 0
	$maxTries = 10
	$user = $username
	$pwd = $password
	$wsPath = "C:\python38\scripts\withings-sync.exe"

	# discord setup
	$discordBot = "Withings -> Garmin Sync"
	$hookUrl = "https://discord.com/api/webhooks/xxxx"
	$content = "{0:HH:mm:ss}: Garmin upload beginning for $user." -f ( (Get-Date) )
	$section = New-DiscordSection -Title "Info" -Description $content -Color LightGrey

	# send discord update on start
	Write-Host $content
	Send-DiscordMessage -webHookUrl $hookUrl -Sections $section -AvatarName $discordBot

	do
	{
		# only retry $maxTries times
		if($countOfTries -lt $maxTries)
		{
			# create args, then try withings-sync upload and capture output
			$args = ("--garmin-username ${user} --garmin-password ${pwd} --fromdate {0:yyyy-MM-dd}" -f $fromDate).ToString()			
			$process = Execute-Command -commandTitle "Withings->Garmin Sync" -commandPath $wsPath -commandArguments $args
			$outputText = $process.stdout
			Write-Host $outputText

			# if upload worked, break loops
			if( $outputText.Contains("Fit file uploaded to Garmin Connect") )
			{
				$content = "{0:HH:mm:ss}: Garmin upload successful for $user." -f ( (Get-Date) )
				$section = New-DiscordSection -Title "Info" -Description $content -Color Green
				$loop = $false
			}
			# if no measurements, wait and try again later
			elseif( $outputText.Contains("No measurements to upload for date or period specified") )
			{
				$content = "{0:HH:mm:ss}: No Withings measurements found for $user; sleeping $hoursToPause hour(s) and trying again." -f ( (Get-Date) )
				$section = New-DiscordSection -Title "Alert" -Description $content -Color Yellow
			}
			# if withings refresh failed
			elseif( $outputText.Contains("withings - ERROR") )
			{
				$content = "{0:HH:mm:ss}: Withings refresh failed for $user; sleeping $hoursToPause hour(s) and trying again." -f ( (Get-Date) )
				$section = New-DiscordSection -Title "Alert" -Description $content -Color Red
			}
			# if garmin upload failed
			else
			{
				$content = "{0:HH:mm:ss}: Garmin upload failed for $user; sleeping $hoursToPause hour(s) and trying again." -f ( (Get-Date) )
				$section = New-DiscordSection -Title "Alert" -Description $content -Color Red	
			}
			
			# send discord update
			Write-Host $content
			Send-DiscordMessage -webHookUrl $hookUrl -Sections $section -AvatarName $discordBot
			
			# pause for $countOfTries hour(s) and try again
			$countOfTries = $countOfTries + 1
			if($loop) { Start-Sleep ( $hoursToPause * 60 * 60 ) }
		}
		# after #maxTries failures, send discord update and exit the loop
		else
		{
			$content = "{0:HH:mm:ss}: Garmin upload failed $maxTries times, aborting." -f ( (Get-Date) )
			$section = New-DiscordSection -Title "Alert" -Description $content -Color Red 
			Write-Host $content
			Send-DiscordMessage -webHookUrl $hookUrl -Sections $section -AvatarName $discordBot
			
			$loop = $false	
		}
	}
	while ($loop -eq $true)
}

Install the PSDiscord powershell module. Set $hookUrl with your Discord Webhook URL. Setup your path to the Python withings-sync executable in the $wsPath variable, then call the withings-sync function from Powershell after initializing your Withings token with withings-sync manually, as per the github instructions (pass your Garmin credentials as the $username and $password parameters). The script will auto-retry up to $maxTries times every $hoursToPause.

Edited 2021-09-21: removed some code from the Execute-Command function that could cause a deadlock when Garmin spits out its 403 error and changed the order of the if/else statements slightly. Cheers!