Hi everyone,
I wanted to share a custom solution I created to improve how Zabbix handles sound alerts.
Personally, I’m not a fan of how Zabbix’s default sound alerts work. So, I built a standalone HTML page that plays a sound every 10 seconds whenever there's at least one unacknowledged problem with a severity above "average".
Here’s how to use it:
This way, the dashboard will play an audible alert when there's something important going on that hasn't been acknowledged.
I’m hoping this solution could be helpful to others who are looking for a way to ensure critical alerts are addressed in a timely manner.
Cheers,
João Pecados
I wanted to share a custom solution I created to improve how Zabbix handles sound alerts.
Personally, I’m not a fan of how Zabbix’s default sound alerts work. So, I built a standalone HTML page that plays a sound every 10 seconds whenever there's at least one unacknowledged problem with a severity above "average".
Here’s how to use it:
- Place the file in your Zabbix frontend directory:
/usr/share/zabbix/ui/SoundAlert.html - Edit the source code of the HTML page:
At the top of the file, you’ll see two variables — zabbixUrl and apiToken.- Create an API token in Zabbix via: Administration > Users > API tokens
- Paste the token and your Zabbix URL into those variables.
- Add to your dashboard:
You can add this to any dashboard using a URL widget.
Just point it to:
https://YourZabbixURL/SoundAlert.html
This way, the dashboard will play an audible alert when there's something important going on that hasn't been acknowledged.
I’m hoping this solution could be helpful to others who are looking for a way to ensure critical alerts are addressed in a timely manner.
Cheers,
João Pecados
PHP Code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zabbix Alert Sound</title>
<script>
const zabbixUrl = "https://YourZabbixURL/api_jsonrpc.php"; // Replace with your actual Zabbix URL
const apiToken = ""; // Replace with your actual Zabbix API token
async function checkAlerts() {
// Step 1: Get Enabled Hosts
const hostResponse = await fetch(zabbixUrl, {
method: "POST",
headers: {
"Content-Type": "application/json-rpc",
"Authorization": `Bearer ${apiToken}`
},
body: JSON.stringify({
"jsonrpc": "2.0",
"method": "host.get",
"params": {
"output": ["hostid", "name"],
"filter": { "status": "0" } // Only enabled hosts
},
"id": 1
})
});
const hostData = await hostResponse.json();
const enabledHosts = new Set(hostData.result.map(host => host.hostid));
console.log("Enabled Hosts:", enabledHosts);
// Step 2: Get Enabled Triggers & Their Hosts
const triggerResponse = await fetch(zabbixUrl, {
method: "POST",
headers: {
"Content-Type": "application/json-rpc",
"Authorization": `Bearer ${apiToken}`
},
body: JSON.stringify({
"jsonrpc": "2.0",
"method": "trigger.get",
"params": {
"output": ["triggerid"],
"selectHosts": ["hostid"], // Fetch the related host
"filter": { "status": "0" } // Only enabled triggers
},
"id": 1
})
});
const triggerData = await triggerResponse.json();
// Create a map of triggerID -> hostID
let triggerHostMap = new Map();
triggerData.result.forEach(trigger => {
if (trigger.hosts.length > 0) {
triggerHostMap.set(trigger.triggerid, trigger.hosts[0].hostid);
}
});
console.log("Trigger-Host Map:", triggerHostMap);
// Step 3: Get All Problems
const problemResponse = await fetch(zabbixUrl, {
method: "POST",
headers: {
"Content-Type": "application/json-rpc",
"Authorization": `Bearer ${apiToken}`
},
body: JSON.stringify({
"jsonrpc": "2.0",
"method": "problem.get",
"params": {
"output": ["eventid", "name", "severity", "objectid"], // objectid = triggerid
"selectAcknowledges": "extend",
"filter": { "acknowledged": "0" }, // Only unacknowledged problems
"suppressed": false,
"sortfield": ["eventid"],
"sortorder": "DESC"
},
"id": 1
})
});
const problemData = await problemResponse.json();
console.log("Raw Problems:", problemData.result);
// Step 4: Filter problems (enabled trigger + enabled host)
const filteredProblems = problemData.result.filter(problem => {
const triggerId = problem.objectid; // This is the triggerid
const hostId = triggerHostMap.get(triggerId);
return hostId && enabledHosts.has(hostId);
});
console.log("Filtered Problems:", filteredProblems);
// Step 5: Display Problems & Play Alert
const problemList = document.getElementById("problems");
problemList.innerHTML = "";
const hasSevereProblem = filteredProblems.some(problem => parseInt(problem.severity) >= 3);
if (filteredProblems.length > 0) {
if (hasSevereProblem) {
playAlert();
}
document.getElementById("status").innerText = "ALERT: Unacknowledged problems detected!";
document.body.style.backgroundColor = "red";
filteredProblems.forEach(problem => {
let problemItem = document.createElement("li");
problemItem.textContent = `${problem.name} (Severity: ${problem.severity})`;
problemList.appendChild(problemItem);
});
} else {
document.getElementById("status").innerText = "No alerts.";
document.body.style.backgroundColor = "green";
}
}
function playAlert() {
const audio = new Audio("audio/alarm_information.mp3"); // Replace with actual sound file
audio.play();
}
window.onload = function() {
checkAlerts();
setInterval(checkAlerts, 10000); // Refresh every 10 seconds
};
</script>
<style>
body { text-align: center; font-family: Arial, sans-serif; color: white; padding: 50px; }
#status { font-size: 24px; font-weight: bold; }
ul { list-style-type: none; padding: 0; }
li { font-size: 20px; margin: 5px 0; }
</style>
</head>
<body>
<h1>Zabbix Sound Alert</h1>
<p id="status">Checking for alerts...</p>
<ul id="problems"></ul>
</body>
</html>