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!