<% ' ============================================================================ ' FUNCTION: SafeGetZabbixData ' PURPOSE: Safely call Zabbix function with error handling ' ============================================================================ Function SafeGetZabbixData(ipaddress) On Error Resume Next Dim result result = GetAllPrinterSuppliesCached(ipaddress) If Err.Number <> 0 Then result = Empty Err.Clear End If On Error Goto 0 SafeGetZabbixData = result End Function theme = Request.Cookies("theme") IF theme = "" THEN theme="bg-theme1" END IF ' ============================================================================ ' FUNCTION: GetMarketingName ' PURPOSE: Convert OEM part numbers to marketing names for easier supply closet matching ' ============================================================================ Function GetMarketingName(oemPartNumber) Dim oem, marketing oem = UCase(Trim(oemPartNumber)) ' HP M454dw / M454dn / M479fdw (414A/414X series) If oem = "W2020A" Or oem = "W2020X" Then marketing = "414A/414X Black" ElseIf oem = "W2021A" Or oem = "W2021X" Then marketing = "414A/414X Cyan" ElseIf oem = "W2022A" Or oem = "W2022X" Then marketing = "414A/414X Yellow" ElseIf oem = "W2023A" Or oem = "W2023X" Then marketing = "414A/414X Magenta" ' HP M254dw / M255dw (202A/202X series) ElseIf oem = "CF500A" Or oem = "CF500X" Then marketing = "202A/202X Black" ElseIf oem = "CF501A" Or oem = "CF501X" Then marketing = "202A/202X Cyan" ElseIf oem = "CF502A" Or oem = "CF502X" Then marketing = "202A/202X Yellow" ElseIf oem = "CF503A" Or oem = "CF503X" Then marketing = "202A/202X Magenta" ' HP M251nw / M252dw (201A/201X series) ElseIf oem = "CF400A" Or oem = "CF400X" Then marketing = "201A/201X Black" ElseIf oem = "CF401A" Or oem = "CF401X" Then marketing = "201A/201X Cyan" ElseIf oem = "CF402A" Or oem = "CF402X" Then marketing = "201A/201X Yellow" ElseIf oem = "CF403A" Or oem = "CF403X" Then marketing = "201A/201X Magenta" ' HP LaserJet 200 color M251nw (131A/131X series) ElseIf oem = "CF210A" Or oem = "CF210X" Then marketing = "131A/131X Black" ElseIf oem = "CF211A" Then marketing = "131A Cyan" ElseIf oem = "CF212A" Then marketing = "131A Yellow" ElseIf oem = "CF213A" Then marketing = "131A Magenta" ' HP M404n / M406 (58A/58X series) ElseIf oem = "CF258A" Or oem = "CF258X" Then marketing = "58A/58X Black" ' HP M506 / M607 (87A/87X series) ElseIf oem = "CF287A" Or oem = "CF287X" Then marketing = "87A/87X Black" ' HP M602 (90A/90X series) ElseIf oem = "CE390A" Or oem = "CE390X" Then marketing = "90A/90X Black" ' HP P3015dn (55A/55X series) ElseIf oem = "CE255A" Or oem = "CE255X" Then marketing = "55A/55X Black" ' HP LaserJet 4250tn (42A/42X series) ElseIf oem = "Q5942A" Or oem = "Q5942X" Then marketing = "42A/42X Black" ' HP LaserJet Pro 4001n (147A/147X series) ElseIf oem = "W1470A" Or oem = "W1470X" Then marketing = "147A/147X Black" ' HP Imaging Drums ElseIf oem = "CF234A" Then marketing = "34A Drum" ElseIf oem = "CF219A" Then marketing = "19A Drum" ElseIf oem = "W2030A" Or oem = "W2030X" Then marketing = "415A/415X Drum" ' HP Maintenance Kits ElseIf oem = "CF254A" Then marketing = "54A Maintenance Kit" ElseIf oem = "CF247A" Then marketing = "47A Maintenance Kit" ' Xerox (note: many use numeric part numbers) ElseIf oem = "006R01697" Then marketing = "Xerox Black Toner" ElseIf oem = "006R01698" Then marketing = "Xerox Cyan Toner" ElseIf oem = "006R01699" Then marketing = "Xerox Yellow Toner" ElseIf oem = "006R01700" Then marketing = "Xerox Magenta Toner" Else marketing = "" ' No mapping found - will display OEM number only End If GetMarketingName = marketing End Function %>
  Supplies Alert Report

Monitors: Toner/Ink <20%, Drums <20%, Maintenance Kits <20%, Waste Cartridges >80%

<% ' Declare all variables at top level to avoid scope issues Dim strSQL, rs, printerid, printerwindowsname, printercsfname, ipaddress, machinenumber, modelnumber, machineid, vendor Dim printerData, zabbixConnected, pingStatus, suppliesJSON Dim lowSuppliesFound Dim alertItems() Dim alertCount Dim itemStart, itemEnd, currentPos, itemBlock Dim itemName, itemValue, itemStatus, itemState Dim namePos, nameStart, nameEnd Dim valuePos, valueStart, valueEnd Dim statusPos, statusStart, statusEnd Dim statePos, stateStart, stateEnd Dim baseName, numericValue Dim statusIcon, statusColor, statusText Dim partNumber, lookupName Dim partNumbers Dim debugPartNumbers, debugAllItems, debugItemCount Dim isSupplyItem, isWasteItem, showItem Dim marketingName, displayPartNumber Dim urgencyScore, alertItem Dim i, j, tempAlert, outputItem, k Dim partKeyName, tryName, partKey, foundMatch Dim primaryWord, supplyType, colorPos Dim typeMatches, colorMatches alertCount = 0 ReDim alertItems(500) ' Pre-allocate space for up to 500 alerts lowSuppliesFound = False strSQL = "SELECT printers.printerid, printers.printerwindowsname, printers.printercsfname, printers.ipaddress, " &_ "machines.machinenumber, machines.machineid, models.modelnumber, machines.alias, vendors.vendor " &_ "FROM printers " &_ "INNER JOIN models ON printers.modelid = models.modelnumberid " &_ "INNER JOIN machines ON printers.machineid = machines.machineid " &_ "INNER JOIN vendors ON models.vendorid = vendors.vendorid " &_ "WHERE printers.isactive = 1 AND printers.ipaddress IS NOT NULL AND printers.ipaddress != '' " &_ "ORDER BY machines.machinenumber ASC" set rs = objconn.Execute(strSQL) While Not rs.EOF printerid = rs("printerid") printerwindowsname = rs("printerwindowsname") printercsfname = rs("printercsfname") ipaddress = rs("ipaddress") modelnumber = rs("modelnumber") machineid = rs("machineid") vendor = rs("vendor") ' Use alias if available, otherwise machinenumber If NOT IsNull(rs("alias")) AND rs("alias") <> "" Then machinenumber = rs("alias") Else machinenumber = rs("machinenumber") End If ' Get cached Zabbix data for this printer (all supplies including maintenance) printerData = SafeGetZabbixData(ipaddress) If Not IsEmpty(printerData) And IsArray(printerData) Then zabbixConnected = printerData(0) pingStatus = printerData(1) suppliesJSON = printerData(2) ' Parse supplies JSON to find items below 20% If zabbixConnected = "1" And suppliesJSON <> "" And InStr(suppliesJSON, """result"":[") > 0 Then ' Check if result array is not empty If InStr(suppliesJSON, """result"":[]") = 0 Then ' First pass: Build lookup of part numbers (type:info items) ' Use Dictionary object for more reliable storage Set partNumbers = Server.CreateObject("Scripting.Dictionary") debugPartNumbers = "" debugAllItems = "" debugItemCount = 0 currentPos = InStr(suppliesJSON, """result"":[") + 11 ' Scan for part number items (containing "Part Number" in name) Do While currentPos > 11 And currentPos < Len(suppliesJSON) itemStart = InStr(currentPos, suppliesJSON, "{""itemid"":") If itemStart = 0 Then Exit Do itemEnd = InStr(itemStart, suppliesJSON, "},{") If itemEnd = 0 Then itemEnd = InStr(itemStart, suppliesJSON, "}]") If itemEnd = 0 Then Exit Do itemBlock = Mid(suppliesJSON, itemStart, itemEnd - itemStart + 1) ' Extract name namePos = InStr(itemBlock, """name"":""") If namePos > 0 Then nameStart = namePos + 8 nameEnd = InStr(nameStart, itemBlock, """") itemName = Mid(itemBlock, nameStart, nameEnd - nameStart) Else itemName = "" End If ' DEBUG: Track all items scanned debugItemCount = debugItemCount + 1 If debugItemCount <= 10 Then debugAllItems = debugAllItems & itemName & " | " End If ' If this is a part number item, store it ' Look for various part number patterns (case-insensitive) If InStr(1, itemName, "Part Number", 1) > 0 Or InStr(1, itemName, "Part number", 1) > 0 Or InStr(1, itemName, "OEM", 1) > 0 Or InStr(1, itemName, "SKU", 1) > 0 Then valuePos = InStr(itemBlock, """lastvalue"":""") If valuePos > 0 Then valueStart = valuePos + 13 valueEnd = InStr(valueStart, itemBlock, """") itemValue = Mid(itemBlock, valueStart, valueEnd - valueStart) ' Store in dictionary with full item name as key (e.g., "Black Toner Part Number") If Not partNumbers.Exists(itemName) Then partNumbers.Add itemName, itemValue debugPartNumbers = debugPartNumbers & "[" & itemName & "=" & itemValue & "] " End If End If End If currentPos = itemEnd + 1 Loop ' Debug disabled - uncomment to show part number matching debug info ' Response.Write("") ' Second pass: Find level items below 20% currentPos = InStr(suppliesJSON, """result"":[") + 11 Do While currentPos > 11 And currentPos < Len(suppliesJSON) ' Find next item itemStart = InStr(currentPos, suppliesJSON, "{""itemid"":") If itemStart = 0 Then Exit Do ' Find end of this item itemEnd = InStr(itemStart, suppliesJSON, "},{") If itemEnd = 0 Then ' Last item in array itemEnd = InStr(itemStart, suppliesJSON, "}]") End If If itemEnd = 0 Then Exit Do itemBlock = Mid(suppliesJSON, itemStart, itemEnd - itemStart + 1) ' Extract item name - "name":" is 8 characters namePos = InStr(itemBlock, """name"":""") If namePos > 0 Then nameStart = namePos + 8 nameEnd = InStr(nameStart, itemBlock, """") itemName = Mid(itemBlock, nameStart, nameEnd - nameStart) Else itemName = "Unknown" End If ' Extract lastvalue - "lastvalue":" is 13 characters valuePos = InStr(itemBlock, """lastvalue"":""") If valuePos > 0 Then valueStart = valuePos + 13 valueEnd = InStr(valueStart, itemBlock, """") itemValue = Mid(itemBlock, valueStart, valueEnd - valueStart) Else itemValue = "0" End If ' Extract status (0 = enabled, 1 = disabled) - "status":" is 10 characters statusPos = InStr(itemBlock, """status"":""") If statusPos > 0 Then statusStart = statusPos + 10 statusEnd = InStr(statusStart, itemBlock, """") itemStatus = Mid(itemBlock, statusStart, statusEnd - statusStart) Else itemStatus = "0" End If ' Extract state (0 = normal, 1 = not supported) - "state":" is 9 characters statePos = InStr(itemBlock, """state"":""") If statePos > 0 Then stateStart = statePos + 9 stateEnd = InStr(stateStart, itemBlock, """") itemState = Mid(itemBlock, stateStart, stateEnd - stateStart) Else itemState = "0" End If ' Convert value to number and check if below 20% On Error Resume Next numericValue = CDbl(itemValue) On Error Goto 0 ' Filter: Only show actual supply level items (must have "Level" in name) isSupplyItem = False If InStr(1, itemName, "Level", 1) > 0 Then ' Exclude non-supply items If InStr(1, itemName, "Part Number", 1) = 0 And _ InStr(1, itemName, "ICMP", 1) = 0 And _ InStr(1, itemName, "ping", 1) = 0 And _ InStr(1, itemName, "loss", 1) = 0 And _ InStr(1, itemName, "response", 1) = 0 And _ InStr(1, itemName, "Hostname", 1) = 0 And _ InStr(1, itemName, "Model", 1) = 0 And _ InStr(1, itemName, "Serial", 1) = 0 And _ InStr(1, itemName, "Location", 1) = 0 And _ InStr(1, itemName, "Firmware", 1) = 0 And _ InStr(1, itemName, "Current", 1) = 0 And _ InStr(1, itemName, " Max", 1) = 0 Then isSupplyItem = True End If End If ' Detect if this is a waste cartridge (works backwards - high % is bad) isWasteItem = (InStr(1, itemName, "Waste", 1) > 0) ' Check if item should be shown based on type showItem = False If isSupplyItem And itemStatus = "0" And itemState = "0" Then If isWasteItem Then ' Waste cartridges: alert when ABOVE 80% (nearly full) If numericValue > 80 And numericValue <= 100 Then showItem = True End If Else ' Regular supplies: alert when BELOW 20% (running low) If numericValue < 20 And numericValue >= 0 Then showItem = True End If End If End If If showItem Then lowSuppliesFound = True ' Determine status indicator If isWasteItem Then ' Waste cartridge status (high % = bad) If numericValue >= 95 Then statusIcon = "zmdi-alert-circle" statusColor = "#ff0000" statusText = "Critical - Nearly Full" ElseIf numericValue >= 90 Then statusIcon = "zmdi-alert-triangle" statusColor = "#ff6600" statusText = "Very High" Else statusIcon = "zmdi-info" statusColor = "#ffaa00" statusText = "High" End If Else ' Regular supply status (low % = bad) If numericValue <= 5 Then statusIcon = "zmdi-alert-circle" statusColor = "#ff0000" statusText = "Critical" ElseIf numericValue <= 10 Then statusIcon = "zmdi-alert-triangle" statusColor = "#ff6600" statusText = "Very Low" Else statusIcon = "zmdi-info" statusColor = "#ffaa00" statusText = "Low" End If End If ' Look up part number for this item partNumber = "-" If partNumbers.Count > 0 Then ' Extract base name for lookup - remove " Level" suffix lookupName = Replace(itemName, " Level", "") lookupName = Trim(lookupName) ' Comprehensive matching strategy for all template versions foundMatch = False ' Strategy 1: EXACT match - NEW template format (preferred) ' "Black Toner Level" → "Black Toner Part Number" ' "Cyan Ink Level" → "Cyan Ink Part Number" ' "Black Drum Level" → "Black Drum Part Number" partKeyName = lookupName & " Part Number" If partNumbers.Exists(partKeyName) Then partNumber = partNumbers(partKeyName) foundMatch = True End If ' Strategy 2: Add " Cartridge" - OLD Xerox template format ' "Black Drum Level" → "Black Drum Cartridge Part Number" ' "Black Toner Level" → "Black Toner Cartridge Part Number" If Not foundMatch Then tryName = lookupName & " Cartridge Part Number" If partNumbers.Exists(tryName) Then partNumber = partNumbers(tryName) foundMatch = True End If End If ' Strategy 3: Replace supply type with "Cartridge" - OLD HP template format ' "Black Toner Level" → "Black Cartridge Part Number" ' "Cyan Ink Level" → "Cyan Cartridge Part Number" If Not foundMatch Then ' Replace common supply types with "Cartridge" If InStr(1, lookupName, "Toner", 1) > 0 Then tryName = Replace(lookupName, "Toner", "Cartridge", 1, -1, 1) & " Part Number" ElseIf InStr(1, lookupName, "Ink", 1) > 0 Then tryName = Replace(lookupName, "Ink", "Cartridge", 1, -1, 1) & " Part Number" ElseIf InStr(1, lookupName, "Drum", 1) > 0 Then tryName = Replace(lookupName, "Drum", "Cartridge", 1, -1, 1) & " Part Number" Else tryName = "" End If If tryName <> "" And partNumbers.Exists(tryName) Then partNumber = partNumbers(tryName) foundMatch = True End If End If ' Strategy 4: Check for "Standard MIB" suffix variation ' "Maintenance Kit Level" → "Maintenance Kit Part Number (Standard MIB)" If Not foundMatch Then tryName = lookupName & " Part Number (Standard MIB)" If partNumbers.Exists(tryName) Then partNumber = partNumbers(tryName) foundMatch = True End If End If ' Strategy 5: Intelligent fuzzy match by type and color If Not foundMatch Then ' Extract primary identifier (first significant word) primaryWord = "" supplyType = "" ' Determine supply type If InStr(1, lookupName, "Toner", 1) > 0 Then supplyType = "Toner" ElseIf InStr(1, lookupName, "Ink", 1) > 0 Then supplyType = "Ink" ElseIf InStr(1, lookupName, "Drum", 1) > 0 Then supplyType = "Drum" ElseIf InStr(1, lookupName, "Waste", 1) > 0 Then supplyType = "Waste" ElseIf InStr(1, lookupName, "Fuser", 1) > 0 Then supplyType = "Fuser" ElseIf InStr(1, lookupName, "Maintenance", 1) > 0 Then supplyType = "Maintenance" End If ' Extract color/identifier (first word before supply type) If supplyType <> "" Then colorPos = InStr(1, lookupName, supplyType, 1) If colorPos > 1 Then primaryWord = Trim(Left(lookupName, colorPos - 1)) End If End If ' Search all keys for matching type and color For Each partKey In partNumbers.Keys If InStr(1, partKey, "Part Number", 1) > 0 Then ' Must match supply type typeMatches = False If supplyType <> "" Then typeMatches = (InStr(1, partKey, supplyType, 1) > 0) Or (InStr(1, partKey, "Cartridge", 1) > 0) Else ' For items without obvious type, just look for any match typeMatches = True End If ' Must match color/identifier if present colorMatches = True If primaryWord <> "" Then colorMatches = (InStr(1, partKey, primaryWord, 1) > 0) End If If typeMatches And colorMatches Then partNumber = partNumbers(partKey) foundMatch = True Exit For End If End If Next End If End If ' Get marketing name for this part number marketingName = GetMarketingName(partNumber) If marketingName <> "" Then ' Show marketing name prominently with OEM number in smaller text displayPartNumber = "" & Server.HTMLEncode(marketingName) & "
" & Server.HTMLEncode(partNumber) & "" Else ' No mapping found, just show OEM number displayPartNumber = Server.HTMLEncode(partNumber) End If ' Calculate urgency score for sorting ' For regular supplies: lower % = higher urgency (5% = 95 urgency) ' For waste: higher % = higher urgency (95% = 95 urgency) If isWasteItem Then urgencyScore = numericValue Else urgencyScore = 100 - numericValue End If ' Store alert data for later sorting alertItem = Array( _ urgencyScore, _ vendor, _ printerid, _ printerwindowsname, _ machineid, _ machinenumber, _ modelnumber, _ numericValue, _ statusColor, _ displayPartNumber, _ itemName _ ) alertItems(alertCount) = alertItem alertCount = alertCount + 1 End If ' Move to next item currentPos = itemEnd + 1 Loop End If End If End If rs.MoveNext Wend ' Sort alerts by urgency (highest urgency first = most critical) ' Simple bubble sort with error handling On Error Resume Next If alertCount > 1 Then For i = 0 To alertCount - 2 For j = 0 To alertCount - i - 2 ' alertItems(j)(0) is the urgency score If Not IsEmpty(alertItems(j)) And Not IsEmpty(alertItems(j + 1)) Then If CDbl(alertItems(j)(0)) < CDbl(alertItems(j + 1)(0)) Then ' Swap items tempAlert = alertItems(j) alertItems(j) = alertItems(j + 1) alertItems(j + 1) = tempAlert End If End If Next Next End If ' Output sorted alerts If alertCount > 0 Then lowSuppliesFound = True For k = 0 To alertCount - 1 If Not IsEmpty(alertItems(k)) And IsArray(alertItems(k)) Then outputItem = alertItems(k) ' Array indices: 0=urgencyScore, 1=vendor, 2=printerid, 3=printerwindowsname, ' 4=machineid, 5=machinenumber, 6=modelnumber, 7=numericValue, ' 8=statusColor, 9=displayPartNumber, 10=itemName Response.Write("") Response.Write("") Response.Write("") Response.Write("") Response.Write("") Response.Write("") Response.Write("") End If Next End If On Error Goto 0 If Not lowSuppliesFound Then Response.Write("") End If objConn.Close %>
Printer Location Model Level Part Number
") ' Response.Write("DEBUG (" & ipaddress & "): Scanned " & debugItemCount & " items | ") ' Response.Write("First 10: " & Server.HTMLEncode(debugAllItems) & "
") ' If debugPartNumbers <> "" Then ' Response.Write("Part Numbers Found: " & Server.HTMLEncode(debugPartNumbers)) ' Else ' Response.Write("No part numbers found!") ' End If ' Response.Write("
" & Server.HTMLEncode(outputItem(3)) & "" & Server.HTMLEncode(outputItem(5)) & "" & Server.HTMLEncode(outputItem(6)) & "" & Round(CDbl(outputItem(7)), 1) & "%" & outputItem(9) & "
") Response.Write("
") Response.Write("No supply issues found - All printers have adequate supplies") Response.Write("