- source: github: https://github.com/cretl/gs1200hp-ctrl
- make sure you have current firmware: https://www.zyxel.com/global/en/support/download?model=gs1200-5hp-v2
- Firmware: V2.00(ABKN.2)C0 (release date: November 09, 2022)
- Checksum:
MD5: 674AA0C4DBC7ACE09F4749036DBA742D
&SHA1: 3D62A5C2582A9FDBCE77A1C147222CFF2F1CF58A
- Checksum:
-
set IP and password in script - fields:
SWITCH_IP
andPASSWORD
-
run:
s@h:~$ chmod +x zyxel.sh # make it executable s@h:~$ ./zyxel.sh status # execute!
s@h:~$ ./zyxel.sh status
Port | 1| 2| 3| 4|
------|---|---|---|---|
State | on| on| on| on|
var port_power =[ 4.2,2.8,0,0]; var total_real_power = 7.0
After logging in and while a user session is active the switch blocks any other connection attempt to the HTTP port. You must successfully log out to be able to login again. If you fail to successfully logout, you must either restart (powercycle) the switch or wait for the session timeout (about 5 minutes).
Script is extended with one function getPOEData()
to load "http://${SWITCH_IP}/poe_data.js"
:
- example output of poe_data.js:
var total_power = 60; var max_led_power = 50; var port_power =[ 4.2,3.1,0,0]; var total_real_power = 7.3;`
#!/bin/bash
# Copyright (C) 2023 Pavel Löbl
#
# Based on https://github.com/cretl/gs1200hp-ctrl/
# - https://github.com/cretl/gs1200hp-ctrl/blob/833d922a31e4daa41e3f6f9b3de3c01b8708b10d/gs1200c.sh
#
# gs1200hp-ctrl-ng is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# gs1200hp-ctrl-ng is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with gs1200hp-ctrl-ng. If not, see <http://www.gnu.org/licenses/>.
# user settings
SWITCH_IP="192.168.88.23"
PASSWORD="👉~~~~~~[PASSWORD]~~~~~~👈"
# global variables
passwordNeedsEncryption=""
cookieJarFile=""
showHelp() {
echo "usage: $0 {on|off|toggle|status} {1|2|3|4|all}"
exit
}
die() {
echo "$@"
switchLogout
rm $cookieJarFile >/dev/null 2>&1
exit 2
}
checkCompatibility() {
compatibleSwitchModel="GS1200-5HP v2"
compatibleSwitchFirmwareVersion=("V2.00(ABKN.1)C0" "V2.00(ABKN.2)C0")
switchInfo=$(curl -s http://${SWITCH_IP}/system_data.js --fail)
[ -z "${switchInfo}" ] && die "compatibility check failed: cannot get switch data"
switchModel=$(echo -n "$switchInfo" | grep model_name | cut -d "'" -f2)
[ -z "${switchModel}" ] && die "compatibility check failed: cannot get switch model"
[ "${switchModel}" != "${compatibleSwitchModel}" ] && die "compatibility check failed: ${switchModel} is not compatible"
switchFirmwareVersion=$(echo -n "$switchInfo" | grep sys_fmw_ver | cut -d "'" -f2)
[ -z "${switchFirmwareVersion}" ] && "compatibility check failed: couldn't get switch firmware version"
for ver in "${compatibleSwitchFirmwareVersion[@]}"; do
[ "$switchFirmwareVersion" == "$ver" ] && passwordNeedsEncryption="1"
done
}
convertCodeToChar() {
printf "\\$(printf '%03o' "$1")"
}
convertCharToCode() {
LC_CTYPE=C printf '%d' "'$1"
}
encryptAdminPw() {
declare -a adminPwArray #array to split password characters into
declare -a adminEncPwArray #array with adapted ("encrypted") password characters codes
declare -a adminEncPwArrayWithRandomChars #final array with random characters added before every character
for ((i=0; i<${#PASSWORD}; i++))
do
adminPwArray[$i]="${PASSWORD:$i:1}"
charCode=$(convertCharToCode ${adminPwArray[$i]})
newCharCode="$(( ${charCode} - ${#PASSWORD} ))"
newChar=$(convertCodeToChar $newCharCode)
adminEncPwArray[$i]="$newChar"
adminEncPwArrayWithRandomChars+="a"
adminEncPwArrayWithRandomChars+="$newChar"
done
adminEncPwArrayWithRandomChars+="a"
PASSWORD="$(printf %s "${adminEncPwArrayWithRandomChars[@]}" $'\n')"
}
switchLogin() {
rm $cookieJarFile >/dev/null 2>&1
[ -n "$passwordNeedsEncryption" ] && encryptAdminPw
responseLogin=$(curl -c ${cookieJarFile} -s "http://${SWITCH_IP}/login.cgi" -X POST --data-urlencode "password=${PASSWORD}" -H "Content-Type: application/x-www-form-urlencoded" -H "Connection: keep-alive" -H "Origin: http://${SWITCH_IP}" -H "Referer: http://${SWITCH_IP}/" -w "%{http_code}" --fail -o /dev/null)
[ "${responseLogin}" -ne "200" ] && die "cannot login"
}
switchLogout() {
responseLogout=$(curl -b ${cookieJarFile} -c ${cookieJarFile} -s "http://${SWITCH_IP}/logout.html" -w "%{http_code}" --fail -o /dev/null)
[ "${responseLogout}" -ne "200" ] && die "logout failed"
rm $cookieJarFile >/dev/null 2>&1
}
getPOEState() {
currActivePoePortsBit=$(curl -b ${cookieJarFile} -s "http://${SWITCH_IP}/port_state_data.js" -H 'Connection: keep-alive' | grep portPoE | grep 'var *portPoE *=')
[ $? -ne 0 ] && die "failed to get port state"
echo $currActivePoePortsBit | cut -d"'" -f 2
}
getPOEData() {
poeData=$(curl -b ${cookieJarFile} -s "http://${SWITCH_IP}/poe_data.js" -H 'Connection: keep-alive')
[ $? -ne 0 ] && die "failed to get poe_data"
echo ''
echo $poeData | cut -d ";" -f 3,4
}
setPoePortBit() {
newMask=$1
responseSetPoePortBit=$(curl -b ${cookieJarFile} -s "http://${SWITCH_IP}/port_state_set.cgi" --data "g_port_state=31&g_port_flwcl=0&g_port_poe=${newMask}&g_port_speed0=0&g_port_speed1=0&g_port_speed2=0&g_port_speed3=0&g_port_speed4=0" -w "%{http_code}" --fail -o /dev/null)
[ "${responseSetPoePortBit}" -ne "200" ] && die "failed to set port state"
}
switchPort() {
port=$1
action=$2
bitfield=$3
port=$((port - 1))
case $action in
on)
bitfield=$((bitfield | 1 << port))
;;
off)
bitfield=$((bitfield & (bitfield - (1 << port))))
;;
toggle)
bitfield=$((bitfield ^ (1 << port)))
;;
esac
echo $bitfield
}
changePortState() {
state=$1
port=$2
poeMask=`getPOEState`
case $port in
1|2|3|4)
poeMask=`switchPort $port $state $poeMask`
;;
all)
for p in 1 2 3 4; do
poeMask=`switchPort $p $state $poeMask`
done
;;
esac
setPoePortBit $poeMask
}
printStatus() {
poeMask=`getPOEState`
echo "Port | 1| 2| 3| 4|"
echo "------|---|---|---|---|"
echo -n "State |"
for port in 0 1 2 3; do
if [ $((poeMask & (1 << port))) -ne 0 ];then
echo -n " on|"
else
echo -n "off|"
fi
done
echo
}
cookieJarFile=`mktemp`
checkCompatibility
case $1 in
on|off|toggle)
[ $# -ne 2 ] && showHelp
case $2 in
1|2|3|4|all)
switchLogin
changePortState $1 $2
switchLogout
;;
*)
showHelp
;;
esac
;;
status)
[ $# -ne 1 ] && showHelp
switchLogin
printStatus
getPOEData
switchLogout
;;
esac
exit 0
old script:
#!/bin/bash
## https://github.com/cretl/gs1200hp-ctrl/blob/833d922a31e4daa41e3f6f9b3de3c01b8708b10d/gs1200c.sh
## ----------------------------
## INFO
## -----
# script version: 0.12a
# script version date: 2021/11/01
# dependencies: Bash, curl, printf
#
# Zyxel GS1200-5HPv2
# firmware version: V2.00(ABKN.1)C0
# ports 1-4 = PoE; port 5 = non-PoE
# switch layout (front view):
# [ p1 ] - [ p2 ] - [ p3 ] - [ p4 ] - [ p5 ]
# values: port1=2^0; port2=2^1; port3=2^2; port4=2^3
# values: port1=1; port2=2; port3=4; port4=8
# combos examples: 0=[off]; 1=[1 on]; 3=[1&2 on]; 5=[1&2&3 on]; 15=[1&2&3&4 on];
#
## ----------------------------
## ----------------------------
## SETTINGS
## --------
SWITCH_IP="192.168.88.23"
PASSWORD="~~~~~👉[CHANGE_IT!]👈~~~~~~"
# global variables
passwordNeedsEncryption=""
cookieJarFile=""
showHelp() {
echo "usage: $0 {on|off|toggle|status} {1|2|3|4|all}"
exit
}
die() {
echo "$@"
switchLogout
rm $cookieJarFile >/dev/null 2>&1
exit 2
}
checkCompatibility() {
compatibleSwitchModel="GS1200-5HP v2"
compatibleSwitchFirmwareVersion=("V2.00(ABKN.1)C0" "V2.00(ABKN.2)C0")
switchInfo=$(curl -s http://${SWITCH_IP}/system_data.js --fail)
[ -z "${switchInfo}" ] && die "compatibility check failed: cannot get switch data"
switchModel=$(echo -n "$switchInfo" | grep model_name | cut -d "'" -f2)
[ -z "${switchModel}" ] && die "compatibility check failed: cannot get switch model"
[ "${switchModel}" != "${compatibleSwitchModel}" ] && die "compatibility check failed: ${switchModel} is not compatible"
switchFirmwareVersion=$(echo -n "$switchInfo" | grep sys_fmw_ver | cut -d "'" -f2)
[ -z "${switchFirmwareVersion}" ] && "compatibility check failed: couldn't get switch firmware version"
for ver in "${compatibleSwitchFirmwareVersion[@]}"; do
[ "$switchFirmwareVersion" == "$ver" ] && passwordNeedsEncryption="1"
done
}
convertCodeToChar() {
printf "\\$(printf '%03o' "$1")"
}
convertCharToCode() {
LC_CTYPE=C printf '%d' "'$1"
}
encryptAdminPw() {
declare -a adminPwArray #array to split password characters into
declare -a adminEncPwArray #array with adapted ("encrypted") password characters codes
declare -a adminEncPwArrayWithRandomChars #final array with random characters added before every character
for ((i=0; i<${#PASSWORD}; i++))
do
adminPwArray[$i]="${PASSWORD:$i:1}"
charCode=$(convertCharToCode ${adminPwArray[$i]})
newCharCode="$(( ${charCode} - ${#PASSWORD} ))"
newChar=$(convertCodeToChar $newCharCode)
adminEncPwArray[$i]="$newChar"
adminEncPwArrayWithRandomChars+="a"
adminEncPwArrayWithRandomChars+="$newChar"
done
adminEncPwArrayWithRandomChars+="a"
PASSWORD="$(printf %s "${adminEncPwArrayWithRandomChars[@]}" $'\n')"
}
switchLogin() {
rm $cookieJarFile >/dev/null 2>&1
[ -n "$passwordNeedsEncryption" ] && encryptAdminPw
responseLogin=$(curl -c ${cookieJarFile} -s "http://${SWITCH_IP}/login.cgi" -X POST --data-urlencode "password=${PASSWORD}" -H "Content-Type: application/x-www-form-urlencoded" -H "Connection: keep-alive" -H "Origin: http://${SWITCH_IP}" -H "Referer: http://${SWITCH_IP}/" -w "%{http_code}" --fail -o /dev/null)
[ "${responseLogin}" -ne "200" ] && die "cannot login"
}
switchLogout() {
responseLogout=$(curl -b ${cookieJarFile} -c ${cookieJarFile} -s "http://${SWITCH_IP}/logout.html" -w "%{http_code}" --fail -o /dev/null)
[ "${responseLogout}" -ne "200" ] && die "logout failed"
rm $cookieJarFile >/dev/null 2>&1
}
getPOEState() {
currActivePoePortsBit=$(curl -b ${cookieJarFile} -s "http://${SWITCH_IP}/port_state_data.js" -H 'Connection: keep-alive' | grep portPoE | grep 'var *portPoE *=')
[ $? -ne 0 ] && die "failed to get port state"
echo $currActivePoePortsBit | cut -d"'" -f 2
}
getPOEState2() {
currActivePoePortsBit=$(curl -b ${cookieJarFile} -s "http://${SWITCH_IP}/poe_data.js" -H 'Connection: keep-alive' | grep port_power | cut -d "=" -f 2 )
[ $? -ne 0 ] && die "failed to get port state2"
echo $currActivePoePortsBit | cut -d"'" -f 2
}
setPoePortBit() {
newMask=$1
responseSetPoePortBit=$(curl -b ${cookieJarFile} -s "http://${SWITCH_IP}/port_state_set.cgi" --data "g_port_state=31&g_port_flwcl=0&g_port_poe=${newMask}&g_port_speed0=0&g_port_speed1=0&g_port_speed2=0&g_port_speed3=0&g_port_speed4=0" -w "%{http_code}" --fail -o /dev/null)
[ "${responseSetPoePortBit}" -ne "200" ] && die "failed to set port state"
}
switchPort() {
port=$1
action=$2
bitfield=$3
port=$((port - 1))
case $action in
on)
bitfield=$((bitfield | 1 << port))
;;
off)
bitfield=$((bitfield & (bitfield - (1 << port))))
;;
toggle)
bitfield=$((bitfield ^ (1 << port)))
;;
esac
echo $bitfield
}
changePortState() {
state=$1
port=$2
poeMask=`getPOEState`
case $port in
1|2|3|4)
poeMask=`switchPort $port $state $poeMask`
;;
all)
for p in 1 2 3 4; do
poeMask=`switchPort $p $state $poeMask`
done
;;
esac
setPoePortBit $poeMask
}
printStatus() {
poeMask=`getPOEState`
echo "Port | 1| 2| 3| 4|"
echo "------|---|---|---|---|"
echo -n "State |"
for port in 0 1 2 3; do
if [ $((poeMask & (1 << port))) -ne 0 ];then
echo -n " on|"
else
echo -n "off|"
fi
done
echo
}
cookieJarFile=`mktemp`
checkCompatibility
switchLogin
printStatus
getPOEState2
switchLogout
#case $1 in
# on|off|toggle)
# [ $# -ne 2 ] && showHelp
# case $2 in
# 1|2|3|4|all)
# switchLogin
# changePortState $1 $2
# switchLogout
# ;;
# *)
# showHelp
# ;;
# esac
# ;;
# status)
# [ $# -ne 1 ] && showHelp
# switchLogin
# printStatus
# switchLogout
# ;;
#esac
exit 0
It's not 100%, but usable.