POE Power Script

Got a little annoyed trying to work with the standard POE monitor function, so wrote this little script to only show POE values for interfaces with POE demand and to calculate the total power draw.

#poe-status
:local poeOutStatus ""
:local interfaceName ""
:local poeOutPower 0
:local poeOutPowerRemainder 0
:local poeOutCurrent 0
:local poeOutVoltage 0
:local poeTotalPower 0
:local poeResult [interface ethernet poe monitor [find] as-value once]

:put ("POE Status:")
:foreach line in=$poeResult do={
    :set poeOutStatus ($line->"poe-out-status")
    :if ($poeOutStatus="powered-on") do={
        :set interfaceName ($line->"name")
        :set poeOutPower (($line->"poe-out-power")/10)
        :set poeOutPowerRemainder (($line->"poe-out-power")-($poeOutPower*10))
        :set poeOutCurrent ($line->"poe-out-current")
        :set poeOutVoltage (($line->"poe-out-voltage")/10)
        :set poeTotalPower ($poeTotalPower+($line->"poe-out-power"))

        :put ("Interface: ".$interfaceName."\t | Power: ".$poeOutPower.".".$poeOutPowerRemainder."W\t | Current: ".$poeOutCurrent."mA\t | Voltage: ".$poeOutVoltage."V")
        
        }

    }
:put ("Total Power: ".($poeTotalPower/10).".".(($poeTotalPower)-((($poeTotalPower/10))*10))."W")



sys script run poe-status

POE Status:
Interface: ether4 | Power: 2.0W | Current: 79mA | Voltage: 26V
Interface: ether6 | Power: 2.6W | Current: 100mA | Voltage: 26V
Interface: ether8 | Power: 3.7W | Current: 72mA | Voltage: 52V
Interface: ether10 | Power: 2.8W | Current: 55mA | Voltage: 52V
Interface: ether14 | Power: 5.1W | Current: 99mA | Voltage: 52V
Interface: ether16 | Power: 4.4W | Current: 85mA | Voltage: 52V
Total Power: 20.6W

Nice! Thanks for sharing.

A revised version, inside are present some hints and semplifications…

{
:local ifWanted   "auto-on|forced-on|off"; # it can be one or more, with | as a separator, between auto-on, forced-on and off
:local ifAlsoIdle  true; # Can be true or false. If true displays also the poe capable interface on idle status
:local ifMode      true; # Can be true or false. If true displays also how the interface mode is set
:local pTotalCurr 0
:local pTotalPowr 0
:local div do={
    :local in  [:tostr $1]
    :local sym [:tostr $2]
    :local int [:pick $in 0 ([:len $in] - 1)]
    :local dec [:pick $in ([:len $in] - 1) [:len $in]]
    :local ret "$int.$dec$sym"
    :if ($ret = ".0$sym") do={:set ret "0.0$sym"}
    :return    $ret
}
:local form do={
    :local in   [:tostr $1]
    :local sym  [:tostr $2]
    :local temp "    $in"
    :return     "$[:pick $temp ([:len $temp] - 4) [:len $temp]]$sym"
}
:put ("POE Status:")
/interface ethernet poe
:foreach line in=[monitor [find where poe-out~$ifWanted] once as-value] do={
    :local ifName   ($line->"name")
    :local ifPOut   ($line->"poe-out")
    :local ifPVol   ($line->"poe-voltage")
    :local ifStatus ($line->"poe-out-status")
    :local pOutTens [$div ($line->"poe-out-voltage") "V"]
    :local pOutCurr [$form ($line->"poe-out-current") "mA"]
    :local pOutPowr [$div ($line->"poe-out-power") "W"]
    :set pTotalCurr ($pTotalCurr + ($line->"poe-out-current"))
    :set pTotalPowr ($pTotalPowr + ($line->"poe-out-power"))
    :local ifStrMod ""
    :if ($ifStatus = "powered-on") do={
        :if ($ifMode) do={:set ifStrMod " Mode: $ifPVol\t $ifPOut\t $ifStatus\t|"}
        :put ("Interface: $ifName\t|$ifStrMod $pOutTens | $pOutCurr | $pOutPowr")
    } else={
        :if ($ifAlsoIdle) do={
            :set ifStrMod $ifStatus
            :if ($ifMode) do={:set ifStrMod "| Mode: $ifPVol\t $ifPOut\t $ifStatus"}
            :put ("Interface: $ifName\t$ifStrMod")
        }
    }

}
:local sysTens ""
:local sysPSUl ""
:local sysPSUh ""
/system health
:if ([:typeof [get psu1-voltage]] != "nil") do={
    :set sysPSUl "PSU low $[$div [get psu1-voltage] "V"]"
    :set sysPSUh " | PSU high $[$div [get psu2-voltage] "V"]"
} else={
    :set sysTens [$div [get voltage] "V"]
}
:put ("Total: $[$form $pTotalCurr "mA"] | $[$div $pTotalPowr "W"]")
:put ("Routerboard: $sysTens$sysPSUl$sysPSUh")
}

Units with more than one PSU (CRS112-8P-4S or 16P or 24P or 48P devices)
on example ifWanted=“auto-on|forced-on|off”, ifAlsoIdle=true and ifMode=true
POE Status:
Interface: ether1 | Mode: low auto-on powered-on | 23.8V | 98mA | 2.3W
Interface: ether2 | Mode: low forced-on powered-on | 23.7V | 120mA | 2.8W
Interface: ether3 | Mode: low auto-on short-circuit
Interface: ether4 | Mode: high auto-on waiting-for-load
Interface: ether5 | Mode: low off disabled
Interface: ether6 | Mode: low auto-on powered-on | 23.7V | 196mA | 4.6W
Interface: ether7 | Mode: low auto-on powered-on | 23.8V | 113mA | 2.6W
Interface: ether8 | Mode: low auto-on powered-on | 23.8V | 109mA | 2.5W
Total: 636mA | 14.8W
Routerboard: PSU low 24.2V | PSU high 48.6V
Units with single PSU (hEX PoE / PowerBOX models)
on example ifWanted=“auto-on|forced-on”, ifAlsoIdle=false and ifMode=false
POE Status:
Interface: ether2 | 27.4V | 145mA | 3.9W
Interface: ether3 | 27.4V | 113mA | 3.0W
Total: 258mA | 6.9W
Routerboard: 27.4V

I notice just now than my revision can be improved:
Total separate values for each PSU1 and PSU2 on 8P/16P/24P/48P devices.

Ooh, I like the $div function and the $ifWanted. Some useful stuff for me here on ROS syntax, thanks.

I noticed on my CRS328 that total useable power capacity is actually dictated by which port block you’re on. So each block of 8 ports has a separate 150w capacity.
Was thinking about adding an option to split the total calculation on x number of ports. Couldn’t think of a way to programmatically determine the split without a user set variable, ROS doesn’t seem to expose/store this info.

Unless POE power capacity is always dependant on an 8 port block on Mikrotik devices, anyone know?

Yes, every device with mult-poe-voltage has the same single 8 port identical blocks inside.

I've updated it for 7.4, particularly the /system/health stuff at the end, to track the intentional changes in the way health reporting works in ROS 7. I tested it on a CRS328-24P and a hEX PoE to handle the dual-power vs single-power case:


{
    :local ifWanted   "auto-on|forced-on|off"; # it can be one or more, with | as a separator, between auto-on, forced-on and off
    :local ifAlsoIdle  true; # Can be true or false. If true displays also the poe capable interface on idle status
    :local ifMode      true; # Can be true or false. If true displays also how the interface mode is set
    :local pTotalCurr 0
    :local pTotalPowr 0
    :local div do={
        :local in  [:tostr $1]
        :local sym [:tostr $2]
        :local int [:pick $in 0 ([:len $in] - 1)]
        :local dec [:pick $in ([:len $in] - 1) [:len $in]]
        :local ret "$int.$dec$sym"
        :if ($ret = ".0$sym") do={:set ret "0.0$sym"}
        :return    $ret
    }
    :local form do={
        :local in   [:tostr $1]
        :local sym  [:tostr $2]
        :local temp "    $in"
        :return     "$[:pick $temp ([:len $temp] - 4) [:len $temp]]$sym"
    }
    :put ("POE Status:")
    /interface ethernet poe
    :foreach line in=[monitor [find where poe-out~$ifWanted] once as-value] do={
        :local ifName   ($line->"name")
        :local ifPOut   ($line->"poe-out")
        :local ifPVol   ($line->"poe-voltage")
        :local ifStatus ($line->"poe-out-status")
        :local pOutTens [$div ($line->"poe-out-voltage") "V"]
        :local pOutCurr [$form ($line->"poe-out-current") "mA"]
        :local pOutPowr [$div ($line->"poe-out-power") "W"]
        :set pTotalCurr ($pTotalCurr + ($line->"poe-out-current"))
        :set pTotalPowr ($pTotalPowr + ($line->"poe-out-power"))
        :local ifStrMod ""
        :if ($ifStatus = "powered-on") do={
            :if ($ifMode) do={:set ifStrMod " Mode: $ifPVol\t $ifPOut\t $ifStatus\t|"}
            :put ("Interface: $ifName\t|$ifStrMod $pOutTens | $pOutCurr | $pOutPowr")
        } else={
            :if ($ifAlsoIdle) do={
                :set ifStrMod $ifStatus
                :if ($ifMode) do={:set ifStrMod "| Mode: $ifPVol\t $ifPOut\t $ifStatus"}
                :put ("Interface: $ifName\t$ifStrMod")
            }
        }
    }
    :local sysPSU ""
    /system health
    :do {
        :local p1v [get [find name="psu1-voltage"] value]
        :set sysPSU "PSU low $p1v V"
        :do {
            :local p2v [get [find name="psu2-voltage"] value]
            :set sysPSU "$sysPSU | PSU high $p2v V"
        } on-error={}
    } on-error={
        :set sysPSU ([get [find name="voltage"] value] . "V")
    }
    :put ("Total: $[$form $pTotalCurr "mA"] | $[$div $pTotalPowr "W"]")
    :put ("Routerboard: $sysPSU")
}

Note also the use of on-error traps instead of typeof testing for nil. As a RouterOS scripting newbie, I don't know how kosher this is, but it seems cleaner than type-testing.

Thanks for the change.
Honestly, I think it is clearer to specify which “error” you expect, rather than generically skipping a piece because “it gives” / “can give” an error…
I’ve always hated «On Error Resume Next» way of programming.
But obviously sometime you are forced to use on-error because the scripting language do not give to you any alternative…

Hello,

I am getting this one below. Is this normal ? I am using Mikrotik hex S

 
> system/routerboard/export 
# 2024-01-16 22:49:56 by RouterOS 7.13.1
# software id = 6H10-A1GS
#
# model = RB760iGS

/system health print
Columns: NAME, VALUE, TYPE
#  NAME         VALUE  TYPE
0  voltage      48.6   V   
1  temperature  69     C

 > sys script run poe-status 
POE Status:
Interface:      | Mode:                  
Interface:      | Mode:                  
Interface:      | Mode:                  
Interface:      | Mode:                  
Interface:      | Mode:                  
Total:    0mA | 0.0W
Routerboard: 48.6V

That model do not have standard “4 PoE port” block, but “i” PoE out on ether5 that act differently.

A bit of a hack, to allow for properly formatted output when interface names length’s differ by more than 1 tab length (8 characters)

the “11” being added to the calculation is the length of the first prefix, "Interface: "

{
#poe-status
:local poeOutStatus ""
:local interfaceName ""
:local poeOutPower 0
:local poeOutPowerRemainder 0
:local poeOutCurrent 0
:local poeOutVoltage 0
:local poeTotalPower 0
:local poeResult [interface ethernet poe monitor [find] as-value once]
:local nameLen 0
:local maxLen 0
:local tabTo 0
:local numTabs 0
:local tab ""

:foreach line in=$poeResult do={
	:set interfaceName ($line->"name")
	:if ([:len $interfaceName] > $maxLen) do={:set maxLen [:len $interfaceName]}
}
:set tabTo ((((($maxLen+11)/8)+1)*8)-1)

:put ("POE Status:")
:foreach line in=$poeResult do={
    :set poeOutStatus ($line->"poe-out-status")
    :if ($poeOutStatus="powered-on") do={
        :set interfaceName ($line->"name")
        :set poeOutPower (($line->"poe-out-power")/10)
        :set poeOutPowerRemainder (($line->"poe-out-power")-($poeOutPower*10))
        :set poeOutCurrent ($line->"poe-out-current")
        :set poeOutVoltage (($line->"poe-out-voltage")/10)
        :set poeTotalPower ($poeTotalPower+($line->"poe-out-power"))
	:set nameLen ([:len $interfaceName] + 11)
        :set numTabs ((($tabTo-$nameLen)/8))
	:for i from=0 to=($numTabs) do={:set tab ($tab."\t")}

        :put ("Interface: ".$interfaceName.$tab." | Power: ".$poeOutPower.".".$poeOutPowerRemainder."W\t | Current: ".$poeOutCurrent."mA\t | Voltage: ".$poeOutVoltage."V")
        
	:set tab ""
        }

    }
:put ("Total Power: ".($poeTotalPower/10).".".(($poeTotalPower)-((($poeTotalPower/10))*10))."W")
}

That’s the output for me.

 > sys script run poe-status1
POE Status:
Total Power: 0.0W

Is it so difficult to understand that it doesn’t work on your model?
What did I write wrong or rude in my previous answer?

Hi, i tested with two,CRS318’s (NetPower), running 7.13:
output-tabformatted.png