#!/bin/sh -e
. /usr/html/axis-cgi/lib/sh-helpers.sh
. /lib/rcscripts/sh/files.sh
GDBUS_POWERD_PROP_GET="gdbus call --system --dest=com.axis.PowerControl.Status --object-path=/com/axis/PowerControl/Status --method=org.freedesktop.DBus.Properties.Get com.axis.PowerControl.Status"
GDBUS_POWERD_METHOD="gdbus call --system --dest=com.axis.PowerControl.Status --object-path=/com/axis/PowerControl/Status --method=com.axis.PowerControl.Status"
GDBUS_GLLDPD_METHOD="gdbus call --system --dest=com.axis.glldp --object-path=/com/axis/glldpd --method=com.axis.glldp"
GDBUS_POLICYKIT_LLDPCLI="gdbus call -y -d com.axis.PolicyKitSystem -o /com/axis/PolicyKitSystem -m com.axis.PolicyKitSystem.ExecuteLldpcli"
GDBUS_POWERSAVE_METHOD="gdbus call --system --dest=com.axis.PowerControl.PowerSave --object-path=/com/axis/PowerControl/PowerSave --method=com.axis.PowerControl.PowerSave"
INFO_AVAILABLE=
# $1 - Return variable
# $2 - Parameter to fetch from dbus
# Get value from gdbus
get_value() {
local value
[ $# -eq 2 ] && [ "$1" ] && [ "$2" ] ||
error 'Incorrect call to get_value'
value=$($GDBUS_POWERD_PROP_GET $2 2>&1) ||
error "Failed to fetch $2: $value"
value=${value#\(<}
value=${value%>,\)}
[ "$value" ] || error "Failed to get value for $2"
[ "$2" = NbrOfDevices ] || INFO_AVAILABLE=y
eval $1=\$value
}
# $1 - Parameter title string to print
# $2 - PoE class to translate to string
# Translate poe class number to descriptive string
print_poeclass_string() {
[ $# -eq 2 ] && [ "$1" ] && [ "$2" ] ||
error 'Incorrect call to print_poeclass_string'
case $2 in
3)
echo "$1 IEEE802.3af"
;
4)
echo "$1 IEEE802.3at"
;
5)
echo "$1 Dual_IEEE802.3at (Axis PoE)"
;
102)
echo "$1 Pre-IEEE802.3bt (71W)"
;
*)
if [ $2 -ge $((0x11)) ] && [ $2 -le $((0x18)) ]; then
poe_class=$(($2 & 0x0F))
echo "$1 IEEE802.3bt single signature class $poe_class"
elif [ $2 -ge $((0x21)) ] && [ $2 -le $((0x25)) ]; then
poe_class=$(($2 & 0x0F))
echo "$1 IEEE802.3bt dual signature class $poe_class"
else
echo "$1 N/A"
fi
;
esac
}
# $1 - Parameter title string to print
# Translate poe class number to descriptive string
print_external_power_string() {
[ $# -eq 1 ] && [ "$1" ] ||
error 'Incorrect call to print_external_power_string'
if [ $1 -eq 1 ]; then
echo "Hardware: External power source"
fi
}
# $1 - PoE class to translate to string
# Translate poe status to descriptive string
print_power_status() {
local power_pfx status
[ $# -eq 1 ] && [ "$1" ] ||
error 'Incorrect call to print_power_status'
power_pfx='Power Status:'
get_value status PowerStatusString
echo
if [ "$status" != "''" ]; then
echo "$power_pfx $status"
else
case $1 in
0)
echo "$power_pfx OK"
;
1)
echo "$power_pfx Discrepancy (Camera is OK but operational temperature is reduced)"
;
2)
echo "$power_pfx Mismatch"
;
*)
echo"$power_pfx N/A"
;
esac
fi
}
# 1 - System description string to split and align
# Find new lines in string and align the lines to parameter title
# Assumes the string uses the same new line characters throughout
align_and_print_description() {
local string delim
[ $# -eq 1 ] && [ "$1" ] ||
error 'Incorrect call to align_and_print_description'
string=$1
printf ' -System Description: '
while :; do
# Find next new line in string
case $string in
*\\r\\n*)
delim='\\r\\n'
;
*\\n\\r*)
delim='\\n\\r'
;
*\\n*)
delim='\\n'
;
*\\r*)
delim='\\r'
;
*)
# No more newline found so print last line
# and exit loop
echo "$string"
break
;
esac
# Print string until newline
echo "${string%%$delim*}"
# Remove part of string before newline
string=${string#*$delim}
# Align all lines to first one
printf ' '
done
}
# Get info about neighbour from glldpd and print.
print_neighbour_info() {
local info name description
info=$($GDBUS_GLLDPD_METHOD.GetNeighbourInfo 2>&1) ||
error "DBus call GetNeighbourInfo failed: $info"
# Parameters are a boolean followed by two strings
# Discard first parameter
info=${info#*, \'}
name=${info%%\', \'*}
[ "$name" ] || error "Failed to get system name"
echo " -System Name: $name"
description=${info#*\', \'}
description=${description%\'\)*}
[ "$description" ] || error "Failed to get system description"
align_and_print_description "$description"
}
# $1 - Port priority to translate to string
# Translate priority to descriptive string
print_prio_string() {
local prio_pfx
[ $# -eq 1 ] && [ "$1" ] ||
error 'Incorrect call to print_prio_string'
prio_pfx=' -Priority:'
case $1 in
0)
echo "$prio_pfx Unknown"
;
1)
echo "$prio_pfx Critical"
;
2)
echo "$prio_pfx High"
;
3)
echo "$prio_pfx Low"
;
*)
echo "$prio_pfx Invalid Value"
;
esac
}
# Get info about the lldp negotiation
print_negotiation_info() {
local info prio requested
info=$($GDBUS_GLLDPD_METHOD.GetPoeInfo 2>&1) ||
error "DBus call GetPoeInfo failed: $info"
# Parameters are a boolean, unsigned int, int, int, boolean, int, unsigned int.
# Interesting parameters are requested power and priority.
# Discard first, second and third parameter
info=${info#(*, *, *, }
requested=${info%%, *}
[ "$requested" ] || error "Failed to get requested power"
printf " -Software Power Requested: %d.%d Watt\n" $(($requested/10)) $(($requested%10))
# Discard fourth and fith parameter
info=${info#*, *, }
prio=${info%%, *}
[ "$prio" ] || error "Failed to get priority"
print_prio_string $prio
}
# Print info from negotiation log
print_failure_info() {
local neg_type power failure_time
while read neg_type power failure_time; do
[ "$neg_type" ] || error "Failed to read negotiation type"
[ "$power" ] || error "Failed to read power"
[ "$failure_time" ] || error "Failed to failure time"
echo " Negotiation Type: $neg_type, Power Received: $power," "Time: $failure_time"
done </lib/persistent/var/lib/powerd/negotiation_log
}
# Get total power consumption from powerd, decode the values and print
print_total_power_consumption() {
local info current_power min_power max_power average_power
# Fetch camera power consumption from powerd
info=$($GDBUS_POWERD_METHOD.TotalPowerConsumption 2>&1) || {
case $info in
*"Total power consumption not measured"*)
# Power is not measured for this camera
return 0
;
*)
error "Dbus call TotalPowerConsumption" "failed: $info"
;
esac
}
# Decode dbus response
# Should be four double values
local IFS=,
set -- $info
[ $# -eq 4 ] && [ "$1" ] && [ "$2" ] && [ "$3" ] && [ "$4" ] ||
error 'Incorrect response from dbus call TotalPowerConsumption'
current_power=$1
current_power=${current_power#\(}
current_power=$(printf "%.1f" $current_power)
[ "$current_power" ] || error 'Failed to decode current power'
min_power=$(printf "%.1f" $2)
[ "$min_power" ] || error 'Failed to decode min power'
max_power=$(printf "%.1f" $3)
[ "$max_power" ] || error 'Failed to decode max power'
average_power=$4
average_power=${average_power%\)}
average_power=$(printf "%.1f" $average_power)
[ "$average_power" ] || error 'Failed to decode average power'
# Do not print consumption if the average is zero since it is not in
# use then. For example lightning pan mcu do not report power if
# poe class is three.
[ "$average_power" = 0.0 ] || {
INFO_AVAILABLE=y
echo "
Current Power Consumption: $current_power Watt
Min Power Consumption: $min_power Watt
Max Power Consumption: $max_power Watt
Average Power Consumption: $average_power Watt"
}
}
# $1 - Device name
# Modify the names to be more descriptive.
print_pretty_name() {
[ $# -eq 1 ] && [ "$1" ] ||
error 'Incorrect call to print_pretty_name'
case $1 in
led0)
printf "\nDevice: IR Illumination\n"
;
irc)
printf "\nDevice: IR Cut Filter\n"
;
*)
printf "\nDevice: $1\n"
;
esac
}
# $1 - Device index
# Get device power consumption from powerd, decode the values and print
print_device_power_consumption() {
local info name current_power min_power max_power average_power
[ $# -eq 1 ] && [ "$1" ] ||
error 'Incorrect call to print_device_power_consumption'
# Fetch device power information from powerd
info=$($GDBUS_POWERD_METHOD.DevicePower $1 2>&1) ||
error "Dbus call DevicePower failed: $info"
# Decode dbus response
# Should be one string followed by four double values
local IFS=,
set -- $info
[ $# -eq 5 ] && [ "$1" ] && [ "$2" ] && [ "$3" ] && [ "$4" ] && [ "$5" ] ||
error 'Incorrect response from dbus call DevicePower'
name=$1
name=${name#\(\'}
name=${name%\'}
[ "$name" ] || error 'Failed to decode device name'
current_power=$(printf "%.1f" $2)
[ "$current_power" ] || error 'Failed to decode device current power'
min_power=$(printf "%.1f" $3)
[ "$min_power" ] || error 'Failed to decode device min power'
max_power=$(printf "%.1f" $4)
[ "$max_power" ] || error 'Failed to decode device max power'
average_power=$5
average_power=${average_power%\)}
average_power=$(printf "%.1f" $average_power)
[ "$average_power" ] || error 'Failed to decode device average power'
INFO_AVAILABLE=y
print_pretty_name $name
echo "Current: $current_power Watt
Min: $min_power Watt
Max: $max_power Watt
Average: $average_power Watt"
}
# Get info about PowerSaving
print_powersaving_info() {
local info support mode
echo "PowerSaving"
info=$($GDBUS_POWERSAVE_METHOD.GetPowerSavingSupport 2>&1) ||
error "DBus call GetPowerSavingSupport failed: $info"
info=${info#(}
support=${info%,)}
[ "$support" ] || error "Failed to get powersaving support"
echo " -Support: $support"
[ $support != true ] || {
info=$($GDBUS_POWERSAVE_METHOD.GetPowerSavingMode 2>&1) ||
error "DBus call GetPowerSavingMode failed: $info"
info=${info#(}
mode=${info%,)}
[ "$mode" ] || error "Failed to get powersaving mode"
echo " -Mode: $mode"
}
}
poe_report_powerd() {
local return_value failed_negotiations number_of_devices lldp_info
[ -x /usr/bin/powerd ] || return 1
title 'Power Management'
# Hardware poe class
get_value return_value PsePoeClass
# Hardware PoE class value -1 indicates PoE info is
# not available for this camera.
[ $return_value -eq -1 ] || {
print_poeclass_string "Hardware:" $return_value
# Software poe class
get_value return_value LldpPoeClass
print_poeclass_string "Software:" $return_value
# Neighbour and negotiation information if software poe class
# is used
[ $return_value -lt 3 ] || {
lldp_info=$($GDBUS_POLICYKIT_LLDPCLI show-neighbors 2>&1) ||
error "DBus call $GDBUS_POLICYKIT_LLDPCLI failed: $lldp_info"
echo "$lldp_info" | sed -e 's#\\n$##;s#\\n#\n#g'
print_negotiation_info
}
# Power status
get_value return_value PowerStatus
print_power_status $return_value
# Power requested
get_value return_value PowerRequested
printf "Power Requested: %.1f Watt\n" $return_value
# Power received
get_value return_value PowerReceived
printf "Power Received: %.1f Watt\n" $return_value
# Negotiation counter
get_value failed_negotiations NegotiationCounter
echo "Failed negotiations: $failed_negotiations"
# Print negotiation failure info
[ $failed_negotiations -le 0 ] || print_failure_info
}
# External power status
get_value return_value ExternalPowerStatus
# ExternalPowerStatus value -1 indicates external power is
# not available for this camera.
[ $return_value -eq -1 ] || {
print_external_power_string $return_value
[ $return_value -eq 0 ] || {
# Power status
get_value return_value PowerStatus
print_power_status $return_value
# Power requested
get_value return_value PowerRequested
printf "Power Requested: %.1f Watt\n" $return_value
# Power received
get_value return_value PowerReceived
printf "Power Received: %.1f Watt\n" $return_value
}
}
# Power Saving info
print_powersaving_info
# Total power consumption
print_total_power_consumption
# Number of devices
get_value number_of_devices NbrOfDevices
# Print devices
[ $number_of_devices -le 0 ] || {
local device_index=0
echo "Device Power Consumption:"
while [ $device_index -lt $number_of_devices ]; do
print_device_power_consumption $device_index
device_index=$(($device_index + 1))
done
}
[ "$INFO_AVAILABLE" ] || echo 'No power information available'
}
poe_report() {
local hw_poe_script=/usr/libexec/poe-detect
local sw_poe_script=/usr/libexec/poe-plus-setup
local pse_type_file=/run/poe/pse_type
local poet_executable=/usr/bin/poet
local poe_class pse_type poe_output
[ -x $hw_poe_script ] ||
[ -x $sw_poe_script ] ||
[ -x $poet_executable ] ||
return 1
title "Power over Ethernet (PoE) Detection"
[ ! -x $hw_poe_script ] || {
if [ -r $pse_type_file.hw ]; then
read poe_class <$pse_type_file.hw
else
read poe_class <$pse_type_file
fi
if [ "$poe_class" ]; then
poe_output=$poe_class
else
poe_output="$hw_poe was empty"
fi
printf "Hardware: %s\n\n" "$poe_output"
}
[ ! -x $sw_poe_script ] || {
if [ -r $pse_type_file.hw ]; then
read poe_class <$pse_type_file
if [ "$poe_class" ]; then
poe_output=$poe_class
else
poe_output="$pse_type_file was empty"
fi
else
poe_output="No negotiation"
fi
printf "Software (LLDP): %s\n" "$poe_output"
}
[ ! -x $poet_executable ] || {
poe_output=$($poet_executable --info 2>&1 || :)
[ "$poe_output" ] ||
poe_output="$poet_executable --info returned nothing"
printf "\n%s\n" "$poe_output"
}
}
if ! exists gpiolib-bitfiddle; then
# Use poe_report for cameras without sysfs gpio handling but with PoE+.
# Use poe_report_powerd for cameras without sysfs gpio and without PoE+
# to display power control information.
poe_report || poe_report_powerd ||
error 'Both glldpd and powerd is missing on the camera'
else
# Use poe_report_powerd for cameras with sysfs gpio handling that have
# either PoE+ or power control
poe_report_powerd || error 'Camera does not have powerd'
fi