Does This Error Trapping Do What I Want?

I am forcing a bunch of Blink cams to take snapshots every 10 minutes.

That part has been working a-ok 98% of the time.

98% because, with Blink, the snapshot requests cannot be made back-to-back.

Instead they need to be spaced about 30 seconds apart or Blink will throw a "System Busy… " error.

Mostly 30 seconds works, but every so often it bites The Big One.

So I tried to add error trapping to the script - hoping to trap the error (which the trap sees as a 409) and then wait 30 seconds and re-issue the request for the same cam.

It actually seems like it might be working, but I do not know enough about the PS environment or Blink to be 100% sure.

What I am hoping for is Somebody Who Knows to take a look at the code and tell me if it, at least, looks like it should wait N seconds and then re-try the same cam

Here’s the code; scroll down to function TakeSnapshotFromOneCamera to see the attempted error trapping.

# PURPOSE: To force a snapshot from each camera in the system every 10 minutes
#
#   NOTES: Code copied from Ryan's(@naryk) much larger, much more comprehensive script.
#
# CHANGES: 2019 04-09: - Modified the Win 7 box thusly:
#                        * Downloaded/installed Windows Management Framework 5.1
#                        * Downloaded/installed .NET Framework 4.7.2 DevPack FROM https://dotnet.microsoft.com/download
#                      - Carved away almost all of Andrew's code leaving just snapshots
#          2019 04-12: - Fixed incorrect references to email and password
#          2019 04-12  - Added error trapping to TakeSnapshotFromOneCamera for "System is busy... code :307", although
#                        the trapping seems to see "409) Conflict" instead. 
#          2019 04-14  - Modified error trapping

# ----------------------------------
# Blink Credentials for your system:

$blinkAccountEmail = "x@y.com"
$blinkAccountPassword = "xxx"

# ----------------------------------
# Determine how many seconds we want to 
# wait between individual snapshots.
# If we try too soon, we get a "System Busy"
# error - which comes back to error trapping
# as a 409 error.

$secondsBetweenSnapshots = 30

# ----------------------------------
# Determine how many times we want to re-try
# telling a given cam to take a snspshot in 
# event of above-mentioned 409 error.

$numberOfSnapshotRetries = 3


# ----------------------------------
# Specify Blink's API Server, this is the URL you are 
# directed to when you are prompted for IFTTT Integration 
# to "Grant Access".
# You can verify this yourself to make sure you are sending 
# the data where you expect it to be

$blinkAPIServer = 'prod.immedia-semi.com'

# Use the server below if you are in Germany
#$blinkAPIServer = 'prde.immedia-semi.com'


# ----------------------------------
# Concoct headers to send to Blink's 
# server for authentication

$authenticationHeaders = 
   @{
    "Host" = "$blinkAPIServer"
    "Content-Type" = "application/json"
    }


# ----------------------------------
# Concoct credential data

$credentials = 
   @{
    "email" = $blinkAccountEmail
    "password" = $blinkAccountPassword
    } | ConvertTo-Json


# ----------------------------------
# Specify login URL of Blink API

$BlinkLoginURL = "https://$blinkAPIServer/login"


# ----------------------------------
# Authenticate credentials with Blink 
# Server and get our token for future requests

$response = Invoke-RestMethod -UseBasicParsing $BlinkLoginURL -Method Post -Headers $authenticationHeaders -Body $credentials
if(-not $response)
    {
    echo "Invalid Blink credentials provided. Please verify email and password in this script's code."
    exit
    }


# ----------------------------------
# Get the object data

$region = $response.region.psobject.properties.name
$authToken = $response.authtoken.authtoken


# ----------------------------------
# Concoct headers to send to Blink's
# server after authentication with our token

$workingHeaders = 
   @{
    "Host" = "$blinkAPIServer"
    "TOKEN_AUTH" = "$authToken"
    }


# ----------------------------------------------------------------------------------------------------
function TakeSnapshotFromOneCamera
{
# PURPOSE: To force one camera to take one snapshot
#   NOTES: 1) Sometimes we do not allow enough time between snapshots
#             and the system is busy.  To deal with that, we allow 
#             up to N attempts to take the snapshot before reporting an error.
 param (
        [Parameter(Mandatory=$true)]
        [string]$NetworkId,
        [string]$CameraId,
        [string]$CameraName
        )
 $finished = $false
 $retryCount = 0
 
 While (-not $finished)
    {
    Try
       {
       $curURL = 'https://rest-'+ $region +".immedia-semi.com/network/$NetworkId/camera/$CameraId/thumbnail"
  
       $response = Invoke-RestMethod -UseBasicParsing $curURL -Method Post -Headers $workingHeaders -ErrorAction Stop
        
       # Get list of cameras
       $curURL = 'https://rest-'+ $region +".immedia-semi.com/network/$NetworkId/camera/$cameraId"    
       $response = Invoke-RestMethod -UseBasicParsing $curURL -Method Get -Headers $workingHeaders -ErrorAction Stop
       #$cameraThumbnail = $response.camera_status.thumbnail
       $finished = $true 
       }
   
    Catch
       {   
       if ($retryCount -ge $numberOfSnapshotRetries)
          {
          $curTimeStamp = Get-Date -DisplayHint DateTime
          $curLineNumber = $_.InvocationInfo.ScriptLineNumber
          $curFailedItem = $_.Exception.ItemName  
          $curExceptionMessage = $_.Exception.Message
          $curInnerExceptionMessage = $_.Exception.InnerException
          Write-Host $curTimeStamp "Line" $curLineNumber $cameraName 
          Write-Host "                  " $curFailedItem $curExceptionMessage
          #Write-Host "                   " $curInnerExceptionMessage
          throw
          }
       else
          {
          if ($retryCount -gt 0)
             {
             write-Host "$CameraName Retries=$retryCount"
             }
          Start-Sleep -s $secondsBetweenSnapshots
          $retryCount++
          }
       }
    }
}


# ----------------------------------------------------------------------------------------------------
function TakeSnapshotFromEachCamera
{
 $curURL = 'https://rest-'+ $region +".immedia-semi.com/api/v1/camera/usage"
 $sync_units = Invoke-RestMethod -UseBasicParsing $curURL -Method Get -Headers $workingHeaders

 foreach($network in $sync_units.networks)
    {
     $cameras = $network.cameras
     $curNetwork_id = $network.network_id 
     $count = 1

     foreach($camera in $cameras)
       {                       
        $curCamera_id = $camera.id
        $curCameraName = $camera.name
        #echo "$curCameraName"
        $count += 1
        TakeSnapshotFromOneCamera -NetworkId $curNetwork_id -CameraId $curCamera_id -CameraName $curCameraName
                   
        # ------------------------------------------------------------
        # We need to wait at least a certain number of seconds between
        # snapshots to avoid "System is busy, please wait","code":307}

        Start-Sleep -s $secondsBetweenSnapshots
       }
    }
 }


# ======================================================================
# MAIN PROCEDURE: Force a snapshot from each camera every 10 minutes
#
# Sleep seconds were strictly trial-and-error to get to a 10 min
# interval between snapshots for the cams - including the seconds
# between snaps to avoid "System is Busy..." errors.


do{   
   $totalCount +=1
   $curTimeStamp = Get-Date -DisplayHint DateTime
   #Write-Host " "
   Write-Host "Count=$totalCount $curTimeStamp"
   #Write-Host "-----------------------------"
   TakeSnapshotFromEachCamera 
   Start-Sleep -s 138
  } while (1)

# ========================= End of Code =================================

Yeah, this will work. I’d be inclined to adjust the methodology a bit there, though. I like the idea of having an attempts counter so that it doesn’t retry indefinitely (maybe have the error handling in the catch{} block check that, and if there’s 3 or more in that value you can handle differently, provide a notification that this camera might be down/broken somehow. If not, just increment the value).

But in terms of skipping that on success, it’s probably just simpler to break out of the loop explicitly. :slight_smile:

[quote quote=151568]Yeah, this will work. I’d be inclined to adjust the methodology a bit there, though. I like the idea of having an attempts counter so that it doesn’t retry indefinitely (maybe have the error handling in the catch{} block check that, and if there’s 3 or more in that value you can handle differently, provide a notification that this camera might be down/broken somehow. If not, just increment the value).

But in terms of skipping that on success, it’s probably just simpler to break out of the loop explicitly. 🙂

[/quote]
Thanks.

I tweaked the code and re-pasted into the OP.

Dropped the time between snaps to 20 seconds to force errors and am running a test now.

# https://powershell.org/forums/topic/does-this-error-trapping-do-what-i-want/

#region Input
    $blinkAccountEmail     = 'confirm@fatbelly.com'
    $blinkAccountPassword  = 'Suedog999'
    $blinkAPIServer        = 'prod.immedia-semi.com'
    $secondsBetweenSnaps   = 600 # PURPOSE: To take a snapshot from each camera in the system every 10 minutes
#endregion


#region Initialize, Authenticate
$authenticationHeaders = @{
    Host           = $blinkAPIServer
    'Content-Type' = 'application/json'
}
$credentials = @{
    email    = $blinkAccountEmail
    password = $blinkAccountPassword
} | ConvertTo-Json
$BlinkLoginURL = "https://$blinkAPIServer/login"
$response = Invoke-RestMethod -UseBasicParsing $BlinkLoginURL -Method Post -Headers $authenticationHeaders -Body $credentials
if($response) {
    Write-Output "Successfully authenticated to $blinkAPIServer"
} else {
    Write-Output "Invalid Blink credentials provided. Please verify email and password in this script's code."
    exit
}
$region    = $response.region.psobject.properties.name
$authToken = $response.authtoken.authtoken
$curURL    = "https://rest-$region.immedia-semi.com/api/v1/camera/usage"
$workingHeaders = @{
    Host       = $blinkAPIServer
    TOKEN_AUTH = $authToken
}
$sync_units   = Invoke-RestMethod -UseBasicParsing $curURL -Method Get -Headers $workingHeaders
#endregion


do{ 
    $curTimeStamp = Get-Date -DisplayHint DateTime
 
    foreach($network in $sync_units.networks) {
        $null = Start-Job -ScriptBlock { # using jobs so that cams on one network don't have to wait for cams on the next network
            $network = $Using:network
            foreach($camera in $network.cameras) { 
                $curURL = "https://rest-$Using:region.immedia-semi.com/network/$($network.network_id)/camera/$($camera.id)/thumbnail"
                $Done   = $false
                do {
                    Try {
                        $response = Invoke-RestMethod -UseBasicParsing $curURL -Method Post -Headers $Using:workingHeaders -ErrorAction Stop
                        "$(Get-Date) - Successfully took snapshot - camera: $($camera.name)"
                        $Done = $true
                    } Catch { 
                        "$(Get-Date) Camera $($camera.name) not ready - waiting 30 sec.."
                        Start-Sleep -Seconds 30
                        $Done = $false
                    } 
                } while (-not $Done)
            } # foreach $camera
        } # job
    } # foreach $network

    while ((Get-Job).HasMoreData -ne $false) { Get-Job | Receive-Job }

    $secondsUsed = (New-TimeSpan -Start $curTimeStamp -End (Get-Date)).TotalSeconds
    $secondsToWait = $secondsBetweenSnaps - $secondsUsed
    "Done - waiting $secondsToWait seconds.."
    Start-Sleep -Seconds $secondsToWait
    ' '
    '###################################################################################'
} while ($true)

Output…

You need to change that pwd…

I amended the OP, but the paste into your post remains.

How about deleting ir?

You need to reset the pw, no matter if the post gets deleted.

You should consider that password burned, and exposed to the entire world.