Skip to content

Instantly share code, notes, and snippets.

@abix-
Created September 2, 2025 15:20
Show Gist options
  • Save abix-/9dc8c78e9556f63c1baa380e006165b7 to your computer and use it in GitHub Desktop.
Save abix-/9dc8c78e9556f63c1baa380e006165b7 to your computer and use it in GitHub Desktop.
uptime.ps1
function Start-InternetUptimeMonitor {
[CmdletBinding()]
param(
[string]$Target = '8.8.8.8',
[int]$SampleIntervalSeconds = 1, # how often to ping (default = 1s)
[int]$RefreshSeconds = 1, # how often to refresh the UI
[int]$TimeoutMilliseconds = 900, # ping timeout
[switch]$NoColor # disable colored output
)
# --- internal state ---
$samples = New-Object System.Collections.Generic.List[object] # each: @{ t = [DateTime]; ok = $true/$false; rtt = [int] }
$win1m = 60
$win15m = 15 * 60
$win1h = 60 * 60
$windows = @(@{Name='1 min'; Sec=$win1m}, @{Name='15 min'; Sec=$win15m}, @{Name='1 hour'; Sec=$win1h})
$ping = [System.Net.NetworkInformation.Ping]::new()
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$lastSample = $sw.Elapsed
$lastRefresh = $sw.Elapsed
# Simple color helpers
function _w { param([string]$text,[string]$color='White')
if ($NoColor) { Write-Host $text; return }
Write-Host $text -ForegroundColor $color
}
function _bar { param([double]$pct,[int]$width=40)
$filled = [int][Math]::Round($pct / 100 * $width)
$empty = $width - $filled
'[' + ('#' * $filled) + ('-' * $empty) + ']'
}
function _prc { param([double]$p) '{0,6:N2} %' -f $p }
# Render the dashboard
function _render {
$now = [DateTime]::UtcNow
$cutMax = $now.AddSeconds(-$win1h)
# prune anything older than the largest window
while ($samples.Count -gt 0 -and $samples[0].t -lt $cutMax) { $samples.RemoveAt(0) }
# compute each window
$stats = foreach ($w in $windows) {
$cut = $now.AddSeconds(-$w.Sec)
$win = $samples.Where({ $_.t -ge $cut })
$total = $win.Count
$ok = ($win | Where-Object { $_.ok }).Count
$pct = if ($total -gt 0) { 100.0 * $ok / $total } else { [double]::NaN }
[pscustomobject]@{
Name = $w.Name
Total = $total
OK = $ok
Uptime = $pct
}
}
$last = if ($samples.Count) { $samples[-1] } else { $null }
$status = if ($last -and $last.ok) { 'ONLINE' } else { 'OFFLINE' }
$rttTxt = if ($last -and $last.rtt -ge 0) { "$($last.rtt) ms" } else { '—' }
Clear-Host
_w ('INTERNET UPTIME MONITOR'.PadRight(60,' ')) 'Cyan'
_w ("Target: $Target Last RTT: $rttTxt Samples kept: $($samples.Count)") 'DarkGray'
if ($NoColor) {
Write-Host ("Status: {0}" -f $status)
} else {
$c = if ($status -eq 'ONLINE') {'Green'} else {'Red'}
_w ("Status: {0}" -f $status) $c
}
Write-Host ''
foreach ($s in $stats) {
$p = $s.Uptime
$pct = if ([double]::IsNaN($p)) { 0 } else { $p }
$bar = _bar -pct $pct
$line = "{0,-7} {1} {2} (OK {3}/{4})" -f $s.Name, (_prc $pct), $bar, $s.OK, $s.Total
if ($NoColor) { Write-Host $line }
else {
$col = if ($pct -ge 99) {'Green'} elseif ($pct -ge 95) {'Yellow'} else {'Red'}
_w $line $col
}
}
Write-Host ''
_w ("Ping every {0}s • Refresh every {1}s • Timeout {2}ms • Quit: Ctrl+C" -f $SampleIntervalSeconds, $RefreshSeconds, $TimeoutMilliseconds) 'DarkGray'
}
# Main loop: decouple sampling from rendering
try {
while ($true) {
$now = $sw.Elapsed
# sample at fixed interval
if ( ($now - $lastSample).TotalSeconds -ge $SampleIntervalSeconds ) {
$lastSample = $now
$t = [DateTime]::UtcNow
try {
$reply = $ping.Send($Target, $TimeoutMilliseconds)
$ok = ($reply.Status -eq [System.Net.NetworkInformation.IPStatus]::Success)
$rtt = if ($ok) { [int]$reply.RoundtripTime } else { -1 }
} catch {
$ok = $false; $rtt = -1
}
$samples.Add([pscustomobject]@{ t = $t; ok = $ok; rtt = $rtt })
}
# refresh UI
if ( ($now - $lastRefresh).TotalSeconds -ge $RefreshSeconds ) {
$lastRefresh = $now
_render
}
Start-Sleep -Milliseconds 50
}
}
finally {
$ping.Dispose()
}
}
Start-InternetUptimeMonitor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment