Example: Reading network statistics from DrayTek Vigor router

Hi everybody!

I’d like to share my approach on reading usage data from my DrayTek Vigor (2130) using the exec binding and a small rule. This should work with small changes with other vigor routers as well.

We need a couple of things:

First, the Vigor router needs a user account (as opposed to the default admin account, System Maintenance -> User Passwort). We need this, because if we used the admin user, we would be thrown out of the admin web console everytime the script ran. Note that although the ‘user’ account does not show the necessary diagnostic data in the web console, the XHR urls can still be used. You should also enable ssl access.

Next, we need a script handling the authentication:

/etc/openhab2/exec/vigorGet:

#!/bin/bash

URL=$1
COOKIE=$2
BASEURL=https://IP_OF_ROUTER/cgi-bin/webstax

RESULT=$(curl --write-out "%{http_code}" --silent --insecure -b $COOKIE $BASEURL/$URL)

if [[ "$RESULT" == "302" ]]; then
    curl --silent --fail --data 'aa=dXNlcg==&ab=XXXXXXX' --insecure -o /dev/null -c $COOKIE $BASEURL/login/login
    RESULT=$(curl --silent --write-out "%{http_code}" --insecure -b $COOKIE $BASEURL/$URL)
fi

if [[ "$RESULT" == "302" ]]; then
    exit 1
fi

echo "$RESULT"

You need to adjust the BASEURL and replace the XXXXXX with your base64 encoded password (of course you can just look at the request using your browser’s dev tools to get the encoded password), the user name is always the same (base64 for ‘user’).

This script tries to open a connection using the provided cookie path, if that fails it reauthenticates and stores the cookie. It returns the result of the call, the Vigor uses numbers split with / and |.

Next, we need a corresponding thing:
/etc/openhab2/things/vigor.things

Thing exec:command:vigorPorts [command="/etc/openhab2/exec/vigorGet stat/ports /etc/openhab2/exec/cookie", interval=60, timeout=5, autorun=false]

This runs every minute to get the actual data traveled over the ports.

Next, the items:
/etc/openhab2/items/vigor.items

String vigorPortsRaw "[%s]" (All) {channel="exec:command:vigorPorts:output"} 

Number vigor_wan_bytes_receive "[%d]" (gChart)
Number vigor_wan_bytes_transmit "[%d]" (gChart)
Number vigor_wan_errors_receive "[%d]" (gChart) 
Number vigor_wan_errors_transmit "[%d]" (gChart)
Number vigor_wan_drops_receive "[%d]" (gChart)
Number vigor_wan_drops_transmit "[%d]" (gChart)
Number vigor_wan_filtered "[%d]" (gChart)

Number vigor_lan1_bytes_receive "[%d]" (gChart)
Number vigor_lan1_bytes_transmit "[%d]" (gChart)
Number vigor_lan1_errors_receive "[%d]" (gChart)
Number vigor_lan1_errors_transmit "[%d]" (gChart)
Number vigor_lan1_drops_receive "[%d]" (gChart)
Number vigor_lan1_drops_transmit "[%d]" (gChart)
Number vigor_lan1_filtered "[%d]" (gChart)

Number vigor_lan2_bytes_receive "[%d]" (gChart)
Number vigor_lan2_bytes_transmit "[%d]" (gChart)
Number vigor_lan2_errors_receive "[%d]" (gChart)
Number vigor_lan2_errors_transmit "[%d]" (gChart)
Number vigor_lan2_drops_receive "[%d]" (gChart)
Number vigor_lan2_drops_transmit "[%d]" (gChart)
Number vigor_lan2_filtered "[%d]" (gChart)

Number vigor_lan3_bytes_receive "[%d]" (gChart)
Number vigor_lan3_bytes_transmit "[%d]" (gChart)
Number vigor_lan3_errors_receive "[%d]" (gChart)
Number vigor_lan3_errors_transmit "[%d]" (gChart)
Number vigor_lan3_drops_receive "[%d]" (gChart)
Number vigor_lan3_drops_transmit "[%d]" (gChart)
Number vigor_lan3_filtered "[%d]" (gChart)

Number vigor_lan4_bytes_receive "[%d]" (gChart)
Number vigor_lan4_bytes_transmit "[%d]" (gChart)
Number vigor_lan4_errors_receive "[%d]" (gChart)
Number vigor_lan4_errors_transmit "[%d]" (gChart)
Number vigor_lan4_drops_receive "[%d]" (gChart)
Number vigor_lan4_drops_transmit "[%d]" (gChart)
Number vigor_lan4_filtered "[%d]" (gChart)

Only one item (the raw items) is connected to the script, the others will be set using a rule, to prevent unnecessary network load. gChart is the group whose members get presisted into th InfluxDb in my setup.

Finally, the rule:
/etc/openhab2/rules/vigor.rule

rule traffic_overview
when
  Item vigorPortsRaw changed
then
  var raw = vigorPortsRaw.state.toString().split("[|/]")

	postUpdate(vigor_wan_bytes_receive, raw.get(3))
	postUpdate(vigor_wan_bytes_transmit, raw.get(4))
	postUpdate(vigor_wan_errors_receive, raw.get(5))
	postUpdate(vigor_wan_errors_transmit, raw.get(6))
	postUpdate(vigor_wan_drops_receive, raw.get(7))
	postUpdate(vigor_wan_drops_transmit, raw.get(8))
	postUpdate(vigor_wan_filtered, raw.get(9))

	postUpdate(vigor_lan1_bytes_receive, raw.get(13))
	postUpdate(vigor_lan1_bytes_transmit, raw.get(14))
	postUpdate(vigor_lan1_errors_receive, raw.get(15))
	postUpdate(vigor_lan1_errors_transmit, raw.get(16))
	postUpdate(vigor_lan1_drops_receive, raw.get(17))
	postUpdate(vigor_lan1_drops_transmit, raw.get(18))
	postUpdate(vigor_lan1_filtered, raw.get(19))

	postUpdate(vigor_lan2_bytes_receive, raw.get(23))
	postUpdate(vigor_lan2_bytes_transmit, raw.get(24))
	postUpdate(vigor_lan2_errors_receive, raw.get(25))
	postUpdate(vigor_lan2_errors_transmit, raw.get(26))
	postUpdate(vigor_lan2_drops_receive, raw.get(27))
	postUpdate(vigor_lan2_drops_transmit, raw.get(28))
	postUpdate(vigor_lan2_filtered, raw.get(29))

	postUpdate(vigor_lan3_bytes_receive, raw.get(33))
	postUpdate(vigor_lan3_bytes_transmit, raw.get(34))
	postUpdate(vigor_lan3_errors_receive, raw.get(35))
	postUpdate(vigor_lan3_errors_transmit, raw.get(36))
	postUpdate(vigor_lan3_drops_receive, raw.get(37))
	postUpdate(vigor_lan3_drops_transmit, raw.get(38))
	postUpdate(vigor_lan3_filtered, raw.get(39))

	postUpdate(vigor_lan4_bytes_receive, raw.get(43))
	postUpdate(vigor_lan4_bytes_transmit, raw.get(44))
	postUpdate(vigor_lan4_errors_receive, raw.get(45))
	postUpdate(vigor_lan4_errors_transmit, raw.get(46))
	postUpdate(vigor_lan4_drops_receive, raw.get(47))
	postUpdate(vigor_lan4_drops_transmit, raw.get(48))
	postUpdate(vigor_lan4_filtered, raw.get(49))
end

No surprises here, we just split the string and put the values into the corresponding items, dropping any unneeded values (bytes is sufficient, we don’t need the number of packages as well).

Passed into a nice Grafana graph, this is the result:

lan 3 is not plugged in, so there are no datapoints currently.

Of course, the same mechanism can be use to parse other data from vigor, like number of open sessions, etc.

Some examples for the 2130 (use the path as first parameter for the script to look at the output):

  • session table: stat/session?line=32768
  • data flow for single clients: config/dig_datam
  • dhcp table: stat/grocx_dhcp_status
  • detailed port statistics: stat/port?port=X (replace X with 1 for WAN, or 2-5 for LAN ports)

Since now, we have an overview over current internet usage, we can also do things dynamically, like only checking internet speed via speedport-cli when no big internet usage (as not to disrupt tv streaming), or dynamically starting or throttling backups according to current usage.

Happy logging!

4 Likes