Ad Widget

Collapse

Custom Zabbix Page: Sound Alerts for Unacknowledged High Severity Problems

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • joaopecados
    Junior Member
    • Apr 2025
    • 1

    #1

    Custom Zabbix Page: Sound Alerts for Unacknowledged High Severity Problems

    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:
    1. Place the file in your Zabbix frontend directory:
      /usr/share/zabbix/ui/SoundAlert.html
    2. 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.
    3. 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}`
    },
    bodyJSON.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}`
    },
    bodyJSON.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.triggeridtrigger.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}`
    },
    bodyJSON.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(checkAlerts10000); // Refresh every 10 seconds
    };
    </
    script>
    <
    style>
    body text-aligncenterfont-familyArialsans-serifcolorwhitepadding50px; }
    #status { font-size: 24px; font-weight: bold; }
    ul { list-style-typenonepadding0; }
    li font-size20pxmargin5px 0; }
    </
    style>
    </
    head>
    <
    body>
    <
    h1>Zabbix Sound Alert</h1>
    <
    p id="status">Checking for alerts...</p>
    <
    ul id="problems"></ul>
    </
    body>
    </
    html
Working...