The following scripts offered by William Rolison (owner of Wonderdog Training, LLC):
The script gets hard disk status information from Hard Disk Sentinel when an alert triggered and uses Gotify service.
To use it, please copy/paste the following code and save in a file C:\Scripts\HDSentinelGotify.ps1
Then in Hard Disk Sentinel, select Configuration -> Message Settings -> Run external application or batch file and specify the command line:
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Unrestricted -File "C:\Scripts\HDSentinelGotify.ps1"
On Configuration -> Alerts page, it is possible to select which event(s) should trigger: under the Send/display message, it is possible to select (for example) high temperature, low health and so.
# Hard Disk Sentinel Environment Variables
# https://www.hdsentinel.com/help/en/26_c_msg.html
# Test Values
<#
$env:HDS_Host = 'COMPUTER'
$env:HDS_Alert = 'Overheat'
$env:HDS_Disk = "Disk: #06: Samsung SSD 870 EVO 1TB: 45 °C (Overheat);Disk: #07: Samsung SSD 870 EVO 1TB: 47 °C (Overheat)"
$env:HDS_Threshold = 'Temperature threshold setting: 42 °C'
$env:HDS_Health = 'Disk: #00: Samsung SSD 980 PRO 1TB: 88 %;Disk: #01: Sabrent Rocket 4.0 2TB: 100 %'
#>
# Define variables for Gotify server and App token
$GotifyServer = "https://gotify.example.com"
$Apptoken = "your_token"
Function Send-GotifyMessage {
<#
.SYNOPSIS
Sends a message to the Gotify server.
.PARAMETER GotifyServer
The URL of the Gotify server.
.PARAMETER Apptoken
The application token for authentication.
.PARAMETER Title
The title of the message.
.PARAMETER Message
The content of the message.
.PARAMETER Priority
The priority level of the message (Min, Low, Normal, High).
.EXAMPLE
Send-GotifyMessage -GotifyServer "https://gotify.example.com" -Apptoken "your_token" -Title "Alert" -Message "Disk is overheating!" -Priority "High"
#>
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True, Position = 0)]
[string]$GotifyServer,
[Parameter(Mandatory = $True, Position = 1)]
[string]$Apptoken,
[Parameter(Mandatory = $True, Position = 2)]
[string]$Title = $(Read-Host "Enter Title"),
[Parameter(Mandatory = $True, Position = 3)]
[string]$Message = $(Read-Host "Enter message"),
[Parameter(Mandatory = $True, Position = 4)]
[ValidateSet("Min", "Low", "Normal", "High")]
[string]$Priority
)
# Validate the Gotify server URL
if (-not [Uri]::IsWellFormedUriString($GotifyServer, [UriKind]::Absolute)) {
throw "Invalid Gotify server URL."
}
# Hash Table setting Urgency keys to an integer value
$Urgency = @{
"Min" = 1
"Low" = 3
"Normal" = 6
"High" = 7
}
# Set message urgency based on priority
$Msgurgency = $Urgency[$Priority]
# Create the message body
$body = @{
"title" = $Title
"message" = $Message
"priority" = $Msgurgency
} | ConvertTo-Json
# Send the message using Invoke-RestMethod with error handling
try {
$response = Invoke-RestMethod -Uri "$GotifyServer/message?token=$Apptoken" -Method Post -Body $body -ContentType "application/json; charset=utf-8" -ErrorAction Stop
Write-Host "Message sent successfully: $($response.id)"
} catch {
Write-Host "Failed to send message: $_"
}
}
# Prepare the message with multi-line formatting
# Assuming $env:HDS_Disk and $env:HDS_Health contain multiple entries separated by a comma or another delimiter
$diskEntries = $env:HDS_Disk -split ';' # Adjust the delimiter as necessary
$diskList = $diskEntries -join "`n" # Join entries with a newline character
$healthEntries = $env:HDS_Health -split ';' # Adjust the delimiter as necessary
$healthList = $healthEntries -join "`n" # Join entries with a newline character
$messageContent = "$diskList`n`n$env:HDS_Threshold`n`nOverall Disk Health:`n`n$healthList"
# Call the Send-GotifyMessage function to send the prepared message to the Gotify server
Send-GotifyMessage -GotifyServer $GotifyServer -Apptoken $Apptoken -Title "$($env:HDS_Host): $($env:HDS_Alert)" -Message $messageContent -Priority "High"
Example image

The following script can be used to receive Pushover notifications when any hard disk drive / SSD triggers an alert in Hard Disk Sentinel Pro.
This is an advanced version of the Use Pushover notification service for alerts triggered in Hard Disk Sentinel script.
<#
.SYNOPSIS
Send a Pushover notification using Hard Disk Sentinel environment variables.
.NOTES
- Accepts parameter overrides (useful for scheduled tasks).
- Tries Process -> User -> Machine env scopes.
- Use -DebugMode to see where values were found.
- Supports multiple values for HDS_Disk and HDS_Health (formatted nicely).
- Uses HttpClient with UTF-8 form encoding to avoid mojibake (e.g., °C).
#>
param(
[string]$HDS_TimeStampParam,
[string]$HDS_HostParam,
[string]$HDS_AlertParam,
[string]$HDS_DiskParam,
[string]$HDS_ThresholdParam,
[string]$HDS_HealthParam,
[switch]$DebugMode
)
# --- Configuration ---
$HDSDateTimeFormat = 'yyyyMMdd HHmmss'
$PushoverApiUri = 'https://api.pushover.net/1/messages.json'
$deg = [char]0x00B0
# NOTE: Keep secrets out of checked-in scripts - replace these with secure storage in production
$PushoverToken = 'Token'
$PushoverUserKey = 'UserKey'
# --- Optional test values (comment these out in production) ---
<#
$env:HDS_TimeStamp = '20190220 222023'
$env:HDS_Host = 'COMPUTER'
$env:HDS_Alert = 'Overheat'
$env:HDS_Disk = "Disk: #06: Samsung SSD 870 EVO 1TB: 45 $deg C (Overheat);Disk: #07: Samsung SSD 870 EVO 1TB: 47 $deg C (Overheat)"
$env:HDS_Threshold = "Temperature threshold setting: 42 $deg C"
$env:HDS_Health = 'Disk: #00: Samsung SSD 980 PRO 1TB: 88 %;Disk: #01: Sabrent Rocket 4.0 2TB: 100 %'
#>
# --- Helpers ---
function Test-UriNot404 {
param(
[Parameter(Mandatory)][string]$Uri
)
try {
if ([string]::IsNullOrWhiteSpace($Uri)) {
Write-Host "Error: URI is null or empty." -ForegroundColor Red
return $false
}
if (-not [System.Uri]::IsWellFormedUriString($Uri, [System.UriKind]::Absolute)) {
Write-Host "Error: Invalid URI format: '$Uri'." -ForegroundColor Red
return $false
}
Add-Type -AssemblyName System.Net.Http -ErrorAction SilentlyContinue
$client = [System.Net.Http.HttpClient]::new()
$request = [System.Net.Http.HttpRequestMessage]::new('HEAD', $Uri)
$response = $client.SendAsync($request).Result
if ($response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
Write-Host "Error: URI returned 404 Not Found: $Uri" -ForegroundColor Red
return $false
}
return $true
}
catch {
Write-Host "Error validating URI '$Uri': $_" -ForegroundColor Red
return $false
}
finally {
if ($request) { $request.Dispose() }
if ($client) { $client.Dispose() }
}
}
function Test-PushoverCredentials {
param(
[string]$ApiUri,
[string]$Token,
[string]$UserKey
)
try {
Add-Type -AssemblyName System.Net.Http -ErrorAction SilentlyContinue
$client = [System.Net.Http.HttpClient]::new()
$form = @{
token = $Token
user = $UserKey
message = 'Credential check - ignore this message'
}
$pairs = New-Object 'System.Collections.Generic.List[System.Collections.Generic.KeyValuePair[string,string]]'
foreach ($k in $form.Keys) {
$pairs.Add([System.Collections.Generic.KeyValuePair[string,string]]::new($k, $form[$k]))
}
$content = New-Object System.Net.Http.FormUrlEncodedContent -ArgumentList (,$pairs)
$response = $client.PostAsync($ApiUri, $content).Result
if ($response.StatusCode -eq [System.Net.HttpStatusCode]::BadRequest) {
Write-Host "Error: Pushover credentials (token/user) are invalid or cause HTTP 400 Bad Request." -ForegroundColor Red
return $false
}
elseif (-not $response.IsSuccessStatusCode) {
$errorBody = $response.Content.ReadAsStringAsync().Result
Write-Host "Warning: Pushover API returned status $($response.StatusCode): $errorBody" -ForegroundColor Yellow
return $false
}
return $true
}
catch {
Write-Host "Error validating Pushover credentials: $_" -ForegroundColor Red
return $false
}
finally {
if ($content) { $content.Dispose() }
if ($client) { $client.Dispose() }
}
}
function Get-EnvVar {
param([string]$Name, [switch]$ShowWhereFound)
foreach ($scope in 'Process','User','Machine') {
$val = [Environment]::GetEnvironmentVariable($Name, $scope)
if (-not [string]::IsNullOrWhiteSpace($val)) {
if ($ShowWhereFound) { Write-Host "DEBUG: Found $Name in $scope => $val" }
return $val
}
}
return $null
}
function Resolve-Value {
param([string]$ParamValue, [string]$EnvName)
if (-not [string]::IsNullOrWhiteSpace($ParamValue)) { return $ParamValue }
return Get-EnvVar -Name $EnvName -ShowWhereFound:$DebugMode
}
# Fix common mojibake for degree symbol: remove U+00C2 when it appears before U+00B0
function Fix-DegreeMojibake {
param([string]$s)
if ([string]::IsNullOrEmpty($s)) { return $s }
$c2 = [char]0x00C2
$deg = [char]0x00B0
return ($s -replace ([string]$c2 + $deg), [string]$deg)
}
# --- Resolve inputs (params override env) ---
$values = @{
HDS_TimeStamp = Resolve-Value $HDS_TimeStampParam 'HDS_TimeStamp'
HDS_Host = Resolve-Value $HDS_HostParam 'HDS_Host'
HDS_Alert = Resolve-Value $HDS_AlertParam 'HDS_Alert'
HDS_Disk = Resolve-Value $HDS_DiskParam 'HDS_Disk'
HDS_Threshold = Resolve-Value $HDS_ThresholdParam 'HDS_Threshold'
HDS_Health = Resolve-Value $HDS_HealthParam 'HDS_Health'
}
# --- Validate required values ---
$missing = $values.GetEnumerator() |
Where-Object { [string]::IsNullOrWhiteSpace($_.Value) } |
ForEach-Object { $_.Key }
if ($missing.Count -gt 0) {
Write-Error "Missing environment variables or parameters: $($missing -join ', ')"
if ($DebugMode) {
Write-Host "Hint: run `Get-ChildItem Env:` or `[Environment]::GetEnvironmentVariable('HDS_TimeStamp','User')` to inspect values."
}
exit 1
}
if (-not (Test-UriNot404 -Uri $PushoverApiUri)) {
exit 1
}
if (-not (Test-PushoverCredentials -ApiUri $PushoverApiUri -Token $PushoverToken -UserKey $PushoverUserKey)) {
exit 1
}
# --- Parse timestamp ---
try {
$HDSDateTime = [DateTime]::ParseExact(
$values['HDS_TimeStamp'],
$HDSDateTimeFormat,
[System.Globalization.CultureInfo]::InvariantCulture
)
}
catch {
Write-Error "Failed to parse HDS_TimeStamp ('$($values['HDS_TimeStamp'])') with format $HDSDateTimeFormat. $_"
exit 1
}
# --- Unix timestamp (portable) ---
$epoch = Get-Date '1970-01-01 00:00:00Z'
$PushoverTimestamp = [math]::Round( ($HDSDateTime.ToUniversalTime() - $epoch).TotalSeconds )
# --- Normalize degree mojibake safely ---
$values['HDS_Disk'] = Fix-DegreeMojibake $values['HDS_Disk']
$values['HDS_Threshold'] = Fix-DegreeMojibake $values['HDS_Threshold']
$values['HDS_Health'] = Fix-DegreeMojibake $values['HDS_Health']
# --- Split multi-value fields on ';' ---
$diskEntries = ($values['HDS_Disk'] -split ';') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
$healthEntries = ($values['HDS_Health'] -split ';') | ForEach-Object { $_.Trim() } | Where-Object { $_ }
# --- Build message text ---
$messageText = @()
$messageText += "Disk Status:"
$messageText += ($diskEntries -join "`n")
$messageText += ""
$messageText += $values['HDS_Threshold']
$messageText += ""
$messageText += "Overall Disk Health:"
$messageText += ($healthEntries -join "`n")
# --- Prepare request body (all strings) ---
$body = @{
token = [string]$PushoverToken
user = [string]$PushoverUserKey
message = [string]($messageText -join "`n")
title = [string]("$($values['HDS_Host']): $($values['HDS_Alert'])")
timestamp = [string]$PushoverTimestamp
}
# --- Debug output (masked) ---
if ($DebugMode) {
$masked = $body.Clone()
if ($masked.ContainsKey('token')) { $masked['token'] = '***masked***' }
if ($masked.ContainsKey('user')) { $masked['user'] = '***masked***' }
Write-Host "DEBUG: Request body (pre-send) ="
$masked | Format-List
}
# --- Ensure TLS 1.2 for older Windows ---
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
} catch { }
# --- Send via HttpClient using UTF-8 FormUrlEncodedContent ---
Add-Type -AssemblyName System.Net.Http -ErrorAction SilentlyContinue
# Convert hashtable -> List[KeyValuePair[string,string]] for FormUrlEncodedContent
$pairs = New-Object 'System.Collections.Generic.List[System.Collections.Generic.KeyValuePair[string,string]]'
foreach ($k in $body.Keys) {
$pairs.Add([System.Collections.Generic.KeyValuePair[string,string]]::new($k, [string]$body[$k]))
}
$client = $null
$content = $null
try {
$client = New-Object System.Net.Http.HttpClient
# IMPORTANT: pass the list as a single argument so PS doesn't expand it into multiple params
$content = New-Object System.Net.Http.FormUrlEncodedContent -ArgumentList (,$pairs) # UTF-8 by default
$response = $client.PostAsync($PushoverApiUri, $content).Result
if (-not $response.IsSuccessStatusCode) {
$errBody = $response.Content.ReadAsStringAsync().Result
throw "HTTP $([int]$response.StatusCode) $($response.ReasonPhrase) - $errBody"
}
Write-Host 'Pushover notification sent successfully.' -ForegroundColor Green
}
catch {
Write-Error "Failed to send Pushover notification: $($_.Exception.Message)"
exit 1
}
finally {
if ($content) { $content.Dispose() }
if ($client) { $client.Dispose() }
}
Example image
Did you make any scripts, tools, add-ons which may be useful? Please send an e-mail so it can be shared to help other users.
Heartfelt Development Services Hungary Kft. (HD Sentinel) is providing you with non-supported materials first developed at Heartfelt Development Services Hungary Kft. (HD Sentinel)'s expense which are "Support Materials".
The Support Materials have not been tested by Heartfelt Development Services Hungary Kft. (HD Sentinel) and are not recommended for use in production environments.
Support Materials may have been created using artificial intelligence tools and may contain errors, discrepancies, vulnerabilities or other defects.
Support Materials are provided "as-is" and Heartfelt Development Services Hungary Kft. (HD Sentinel) will not provide any enhancements, bug fixes, updates or other support for the Support Materials.
In addition, the Support Materials are specifically excluded from any warranties, guarantees or other promises provided in your license agreements with Heartfelt Development Services Hungary Kft. (HD Sentinel) or otherwise implied by law.
Heartfelt Development Services Hungary Kft. (HD Sentinel) retains all ownership rights in the Support Materials and hereby grants you a license to copy, use and modify the Support Materials only as needed to assist with your use of the separately-licensed Heartfelt Development Services Hungary Kft. (HD Sentinel) products.