Skip to sidebar Skip to content
Ipercube Technical
Jack of all trades, master of none
Date: Time: - Stardate:
Welcome, Log in by clicking  Here!
SS64
MDN
Stack
Code
GitHub
Wiki
Convert
V Studio
  • Home
  • VB Functions
    • Access Functions
    • Assembly Info
    • Color/Image Functions
    • Data Table Functions
    • Email Functions
    • Active Directory Functions
    • LDAP Functions
    • Excel Functions
    • Logging Functions
    • Source Code Retrieval
    • String Functions
    • Sql Functions
    • Object Utilities Functions
    • System and Utilities
    • Utility Forms
  • VB Classes
    • Environment Variables Helper
  • ABAP
    • ABAP Reports
    • ABAP HTTP Functions
Ipercube Technical
  • Home
  • VB Functions
    • Access Functions
    • Assembly Info
    • Color/Image Functions
    • Data Table Functions
    • Email Functions
    • Active Directory Functions
    • LDAP Functions
    • Excel Functions
    • Logging Functions
    • Source Code Retrieval
    • String Functions
    • Sql Functions
    • Object Utilities Functions
    • System and Utilities
    • Utility Forms
  • VB Classes
    • Environment Variables Helper
  • ABAP
    • ABAP Reports
    • ABAP HTTP Functions

Ups Helper

Provides helper functions to interact with UPS devices via the external tool upsc.exe , part of NUT (Network UPS Tools).

The required upsc.exe and additional DLL can be imported in the project via the NuGet package Ipercube.NUT.Ups.Query.

  • Relies on the presence of upsc.exe in the current working directory.
  • Used to query status and parameters of network-connected UPS devices.
  • Supports extraction and display of key UPS metrics.
VB
Imports System.IO
Imports System.Text

Public NotInheritable Class UpsHelper

    ''' <summary>
    ''' Executes <c>upsc.exe</c> for the given UPS name and returns all key/value
    ''' pairs as a dictionary.
    ''' </summary>
    ''' <category>Methods</category>
    ''' <param name="upsName">UPS name, e.g. <c>"apcups@10.69.1.42"</c>.</param>
    ''' <returns>A <c>Dictionary(Of String, String)</c> with all available key/value pairs.</returns>
    ''' <remarks>
    ''' <list type="bullet">
    '''   <item><description>Throws an exception if <c>upsc.exe</c> is missing or execution fails.</description></item>
    '''   <item><description>Output is parsed line by line; expected format is <c>key: value</c>.</description></item>
    '''   <item><description>If a line has no colon, it is treated as a key with empty value.</description></item>
    '''   <item><description>In case of duplicate keys, the last occurrence overwrites the previous one.</description></item>
    ''' </list>
    ''' </remarks>
    Public Shared Function GetUpsInfo(upsName As String) As Dictionary(Of String, String)

        If String.IsNullOrWhiteSpace(upsName) Then
            Throw New ArgumentException("UPS name is empty.", NameOf(upsName))
        End If

        If Not File.Exists("upsc.exe") Then
            Throw New FileNotFoundException("upsc.exe not found.", "upsc.exe")
        End If

        Dim psi As New ProcessStartInfo() With {
            .FileName = "upsc.exe",
            .Arguments = upsName,
            .UseShellExecute = False,
            .RedirectStandardOutput = True,
            .RedirectStandardError = True,
            .CreateNoWindow = True
        }

        Dim output As String
        Dim err As String

        Using proc As New Process()
            proc.StartInfo = psi

            If Not proc.Start() Then
                Throw New InvalidOperationException("Unable to start upsc.exe process.")
            End If

            output = proc.StandardOutput.ReadToEnd()
            err = proc.StandardError.ReadToEnd()

            proc.WaitForExit()
            Dim exitCode As Integer = proc.ExitCode

            If exitCode <> 0 Then
                Dim msg As New StringBuilder()
                msg.AppendLine("upsc.exe returned a non-zero exit code: " & exitCode.ToString())

                If Not String.IsNullOrWhiteSpace(err) Then
                    msg.AppendLine()
                    msg.AppendLine("Error output:")
                    msg.AppendLine(err.Trim())
                End If

                Throw New ApplicationException(msg.ToString())
            End If
        End Using

        If String.IsNullOrWhiteSpace(output) Then
            Throw New ApplicationException("upsc.exe returned no data.")
        End If

        Dim result As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)

        Dim lines() As String = output.Replace(vbCrLf, vbLf).Split(ControlChars.Lf)
        For Each rawLine As String In lines
            Dim line As String = rawLine.Trim()
            If line.Length = 0 Then Continue For

            ' Expected format: key: value
            Dim idx As Integer = line.IndexOf(":"c)
            If idx <= 0 Then
                ' Line without ':' -> store whole line as key with empty value
                If Not result.ContainsKey(line) Then
                    result(line) = String.Empty
                End If
            Else
                Dim key As String = line.Substring(0, idx).Trim()
                Dim value As String = line.Substring(idx + 1).Trim()
                If Not result.ContainsKey(key) Then
                    result(key) = value
                Else
                    ' In case of duplicated keys, you might want to append or overwrite.
                    ' Here we overwrite with the last occurrence.
                    result(key) = value
                End If
            End If
        Next

        Return result
    End Function

    ''' <summary>
    ''' Executes <c>upsc.exe</c> for the given UPS name and shows all key/value pairs
    ''' in a MessageBox, one per line. Some important keys are shown first.
    ''' </summary>
    ''' <category>Methods</category>
    ''' <param name="upsName">UPS name, e.g. <c>"apcups@10.69.1.42"</c>.</param>
    ''' <remarks>
    ''' <list type="bullet">
    '''   <item><description>Uses <c>GetUpsInfo</c> to extract data from the UPS device.</description></item>
    '''   <item><description>Important keys like battery charge, voltage, and status are shown at the top.</description></item>
    '''   <item><description>Remaining keys are sorted alphabetically and shown below.</description></item>
    '''   <item><description>All values are formatted with units when recognized.</description></item>
    '''   <item><description>Displays the result in a styled HTML MessageBox using <c>ShowMessageBoxEx</c>.</description></item>
    ''' </list>
    ''' </remarks>
    Public Shared Sub ShowUpsInfo(upsName As String)
        Dim info As Dictionary(Of String, String)

        Try
            info = GetUpsInfo(upsName)
        Catch ex As Exception
            ShowMessageBoxEx(ex.Message, "UPS info - Error",
                        CustomButtons.Ok, CustomIcon.ErrorBug)
            Return
        End Try

        If info Is Nothing OrElse info.Count = 0 Then
            ShowMessageBoxEx("No data returned from upsc.exe.", "UPS info",
                        CustomButtons.Ok, CustomIcon.ErrorBug)
            Return
        End If

        ' Important keys to show first (if present)
        Dim importantKeys As String() = {
                                            "ups.mfr",
                                            "ups.model",
                                            "ups.status",
                                            "battery.charge",
                                            "battery.runtime",
                                            "ups.load",
                                            "input.voltage",
                                            "input.frequency",
                                            "battery.voltage",
                                            "ups.realpower.nominal",
                                            "ups.delay.shutdown"
                                        }

        Dim sb As New StringBuilder()
        sb.AppendLine("Key" & vbTab & "Value")

        ' Track printed keys so they are not duplicated later
        Dim printed As New HashSet(Of String)(StringComparer.OrdinalIgnoreCase)

        ' 1) Print important keys (in fixed order)
        For Each k As String In importantKeys
            Dim value As String = Nothing
            If info.TryGetValue(k, value) Then
                value = AddUnitOfMeasure(k, value)
                sb.AppendLine(k & vbTab & value)
                printed.Add(k)
            End If
        Next

        ' 3) Print all keys alphabetically, skipping the already printed ones
        Dim allKeys As List(Of String) = info.Keys.ToList()
        allKeys.Sort(StringComparer.OrdinalIgnoreCase)

        For Each k As String In allKeys
            If printed.Contains(k) Then Continue For

            Dim v As String = info(k)
            v = AddUnitOfMeasure(k, v)
            sb.AppendLine(k & vbTab & v)
        Next

        Dim caption As String = "UPS status - " & upsName

        ShowMessageBoxEx(
            ConvertTabbedString2HtmlPage(sb.ToString()),
            caption,
            CustomButtons.Ok,
            CustomIcon.Statistics,
            layoutKey:="ShowUpsInfo"
        )

    End Sub

    Private Shared Function AddUnitOfMeasure(Key As String, ByVal value As String) As String

        Select Case Key
            Case "battery.runtime"
                Dim totalSeconds As Integer

                If Integer.TryParse(Convert.ToString(value), totalSeconds) Then
                    Dim hours As Integer = totalSeconds \ 3600
                    Dim minutes As Integer = totalSeconds \ 60
                    Dim seconds As Integer = totalSeconds Mod 60
                    If totalSeconds < 3600 Then
                        value = $"{minutes:00}:{seconds:00} (mm:ss)"
                    Else
                        value = $"{hours}:{minutes:00}:{seconds:00} (hh:mm:ss)"
                    End If
                Else
                    value = "00:00"
                End If
            Case "battery.voltage.nominal", "input.voltage.nominal", "input.voltage", "battery.voltage", "input.transfer.high", "input.transfer.low"
                value += " Volt"
            Case "ups.realpower.nominal"
                value += " Watt"
            Case "ups.load", "battery.charge.low", "battery.charge.warning", "battery.charge"
                value += "%"
            Case "ups.delay.shutdown", "battery.runtime.low", "ups.timer.reboot", "driver.parameter.pollfreq", "driver.parameter.pollinterval"
                value += " sec"
            Case "ups.timer.shutdown"
                value = If(value.Trim = "-1", "Disabled", value & " sec")
            Case "ups.status"
                Select Case value
                    Case "OL" : value = "On Line, AC power is available"
                    Case "OB" : value = "Running on Battery"
                    Case "LB" : value = "Battery level LOW"
                    Case "FSD" : value = "Forced Shutdown"
                    Case Else
                        If value.Contains("OB") And value.Contains("OL") Then
                            value = "Running on Battery, Battery level LOW"
                        Else
                            value = "Unknown status: " & value
                        End If
                End Select
        End Select

        Return value
    End Function

End Class
Posted by ipercube


© 2026 Ipercube Design