├── requirements.txt ├── pishut.sh ├── static ├── gps.png ├── power.png ├── sat.png ├── temperature.png └── mobilebroadband.png ├── api ├── application.py ├── config.py └── api.py ├── shutit.sh ├── fortishut.sh ├── nginx ├── s3p ├── openwrt ├── grafana └── zte ├── gpstime.sh ├── supervisord ├── ups.conf ├── sflow.conf ├── w1_therm.conf ├── satinternet.conf ├── mobilebroadband.conf ├── geo.conf └── fgmon.conf ├── fortigate ├── start-mobile.sh ├── stop-mobile.sh └── monitor.py ├── .gitignore ├── influxdb-armhf └── README.md ├── LICENSE ├── grafana-armhf └── README.md ├── ups.py ├── w1_therm.py ├── satinternet.py ├── mobilebroadband.py ├── geo.py ├── traffic.py ├── PyNUT └── __init__.py ├── temp.json ├── README.md ├── traffic.json ├── satinternet.json ├── mobilebroadband.json └── ups.json /requirements.txt: -------------------------------------------------------------------------------- 1 | influxdb 2 | Flask 3 | -------------------------------------------------------------------------------- /pishut.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ssh pi@10.10.1.252 sudo poweroff 4 | -------------------------------------------------------------------------------- /static/gps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ab77/beastcraft-telemetry/HEAD/static/gps.png -------------------------------------------------------------------------------- /static/power.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ab77/beastcraft-telemetry/HEAD/static/power.png -------------------------------------------------------------------------------- /static/sat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ab77/beastcraft-telemetry/HEAD/static/sat.png -------------------------------------------------------------------------------- /static/temperature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ab77/beastcraft-telemetry/HEAD/static/temperature.png -------------------------------------------------------------------------------- /static/mobilebroadband.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ab77/beastcraft-telemetry/HEAD/static/mobilebroadband.png -------------------------------------------------------------------------------- /api/application.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | application = Flask(__name__) 4 | application.config.from_object('config') 5 | application.debug = True 6 | -------------------------------------------------------------------------------- /shutit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | printf "shutting down BeastPi3...\n" 4 | ./pishut.sh 5 | 6 | printf "shutting down BeastGate2...\n" 7 | ./fortishut.sh 8 | -------------------------------------------------------------------------------- /api/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | DEFAULT_TRIES = 3 4 | DEFAULT_DELAY = 2 5 | DEFAULT_BACKOFF = 2 6 | API_VERSION = '1.0' 7 | WORKDIR = '/opt/beastcraft/fortigate' 8 | DEBUG = 1 9 | -------------------------------------------------------------------------------- /fortishut.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect -f 2 | spawn ssh admin@10.10.1.254 3 | send "exec shutdown\r" 4 | expect "Do you want to continue? (y/n)" 5 | send "y\r" 6 | expect "System is shutting down..." 7 | -------------------------------------------------------------------------------- /nginx/s3p: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | server_name s3p-web.beastcraft.belodedenko.me; 5 | 6 | location / { 7 | proxy_pass http://s3p.beastcraft.belodedenko.me/; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /nginx/openwrt: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | server_name beastroute1-web.beastcraft.belodedenko.me; 5 | 6 | location / { 7 | proxy_pass http://beastroute1.beastcraft.belodedenko.me/; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gpstime.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # set initial datetime from GPS 4 | influx -database beastcraft \ 5 | --format csv \ 6 | -execute 'SELECT last("value") FROM "time";' | \ 7 | tail -1 | awk -F',' '{print $3}' | \ 8 | xargs date +%Y-%m-%dT%H:%M:%S.000Z -s 9 | -------------------------------------------------------------------------------- /supervisord/ups.conf: -------------------------------------------------------------------------------- 1 | [program:ups] 2 | command=/usr/bin/python /opt/beastcraft/ups.py 3 | directory=/opt/beastcraft 4 | process_name=%(program_name)s 5 | autostart=true 6 | autorestart=true 7 | stopasgroup=true 8 | stdout_logfile=/var/log/ups.log 9 | redirect_stderr=true 10 | -------------------------------------------------------------------------------- /supervisord/sflow.conf: -------------------------------------------------------------------------------- 1 | [program:sflow] 2 | command=/usr/bin/python /opt/beastcraft/traffic.py 3 | directory=/opt/beastcraft 4 | process_name=%(program_name)s 5 | autostart=true 6 | autorestart=true 7 | stopasgroup=true 8 | stdout_logfile=/var/log/sflowtool.log 9 | redirect_stderr=true 10 | -------------------------------------------------------------------------------- /supervisord/w1_therm.conf: -------------------------------------------------------------------------------- 1 | [program:w1_therm] 2 | command=/usr/bin/python /opt/beastcraft/w1_therm.py 3 | directory=/opt/beastcraft 4 | process_name=%(program_name)s 5 | autostart=true 6 | autorestart=true 7 | stopasgroup=true 8 | stdout_logfile=/var/log/w1_therm.log 9 | redirect_stderr=true 10 | -------------------------------------------------------------------------------- /supervisord/satinternet.conf: -------------------------------------------------------------------------------- 1 | [program:satinternet] 2 | command=/usr/bin/python /opt/beastcraft/satinternet.py 3 | directory=/opt/beastcraft 4 | process_name=%(program_name)s 5 | autostart=true 6 | autorestart=true 7 | stopasgroup=true 8 | stdout_logfile=/var/log/satinternet.log 9 | redirect_stderr=true 10 | -------------------------------------------------------------------------------- /supervisord/mobilebroadband.conf: -------------------------------------------------------------------------------- 1 | [program:mobilebroadband] 2 | command=/usr/bin/python /opt/beastcraft/mobilebroadband.py 3 | directory=/opt/beastcraft 4 | process_name=%(program_name)s 5 | autostart=true 6 | autorestart=true 7 | stopasgroup=true 8 | stdout_logfile=/var/log/mobilebroadband.log 9 | redirect_stderr=true 10 | -------------------------------------------------------------------------------- /supervisord/geo.conf: -------------------------------------------------------------------------------- 1 | [program:geo] 2 | command=/usr/bin/python /opt/beastcraft/geo.py --domain= --key= 3 | directory=/opt/beastcraft 4 | process_name=%(program_name)s 5 | autostart=true 6 | autorestart=true 7 | stopasgroup=true 8 | stdout_logfile=/var/log/gps.log 9 | redirect_stderr=true 10 | -------------------------------------------------------------------------------- /supervisord/fgmon.conf: -------------------------------------------------------------------------------- 1 | [program:fgmon] 2 | command=/usr/bin/python /opt/beastcraft/fortigate/monitor.py --host beastgate2 --iface wifi --backup wan2 --gwip 172.16.100.1 3 | directory=/opt/beastcraft 4 | process_name=%(program_name)s 5 | autostart=true 6 | autorestart=true 7 | stopasgroup=true 8 | stdout_logfile=/var/log/fgmon.log 9 | redirect_stderr=true 10 | -------------------------------------------------------------------------------- /nginx/grafana: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | server_name dash.beastcraft.belodedenko.me; 5 | 6 | location / { 7 | proxy_pass http://localhost:3000/; 8 | } 9 | 10 | location /public/ { 11 | alias /usr/share/grafana/public/; 12 | } 13 | 14 | location /goform/ { 15 | proxy_set_header Referer http://172.17.0.1/; 16 | proxy_pass http://172.17.0.1:80/goform/; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /nginx/zte: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | server_name mf823-web.beastcraft.belodedenko.me; 5 | 6 | location / { 7 | if ( $arg_cmd = 'upgrade_result' ) { 8 | return 200 '{"upgrade_result":"success"}'; 9 | } 10 | 11 | proxy_set_header Referer http://mf823.beastcraft.belodedenko.me/; 12 | proxy_pass http://mf823.beastcraft.belodedenko.me/; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /fortigate/start-mobile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | printf "enabling mobile network...\n" 4 | curl 'http://dash.beastcraft.belodedenko.me/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=CONNECT_NETWORK' 5 | 6 | printf "\nstarting link-monitor...\n" 7 | printf "conf sys link-monitor\nedit wan2\nset status enable\nend\n" \ | 8 | ssh admin@beastgate2.beastcraft.belodedenko.me 9 | 10 | printf "starting fgmon service...\n" 11 | supervisorctl start fgmon 12 | -------------------------------------------------------------------------------- /fortigate/stop-mobile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | printf "stopping link-monitor...\n" 4 | printf "conf sys link-monitor\nedit wan2\nset status disable\nend\n" \ | 5 | ssh admin@beastgate2.beastcraft.belodedenko.me 6 | 7 | printf "diabling mobile network...\n" 8 | curl 'http://dash.beastcraft.belodedenko.me/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=DISCONNECT_NETWORK' 9 | 10 | printf "\nstopping fgmon service...\n" 11 | supervisorctl stop fgmon 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /influxdb-armhf/README.md: -------------------------------------------------------------------------------- 1 | # InfluxDB (armhf) 2 | 3 | ### Debian 4 | 5 | * [influxdb_0.13.0_armhf.deb](https://s3.eu-central-1.amazonaws.com/belodetech/influxdb_0.13.0_armhf.deb) 6 | 7 | ### Fedora/CentOS 8 | 9 | * [influxdb_0.13.0_armhf.rpm](https://s3.eu-central-1.amazonaws.com/belodetech/influxdb-0.13.0.armhf.rpm) 10 | 11 | ### Build 12 | 13 | ``` 14 | # get sources 15 | export VERSION=0.13.0 16 | gvm use go1.5 17 | gvm pkgset create influxdb 18 | gvm pkgset use influxdb 19 | cd ~/.gvm/pkgsets/go1.5/influxdb 20 | export GOPATH=`pwd` 21 | go get github.com/influxdata/influxdb || go get -u github.com/influxdata/influxdb 22 | cd $GOPATH/src/github.com/influxdata/influxdb 23 | git checkout $VERSION 24 | 25 | # build 26 | ./build.py --package --version=$VERSION --arch=armhf 27 | ``` 28 | 29 | ### Install (Debian) 30 | 31 | ``` 32 | dpkg -i build/*.deb 33 | ``` 34 | 35 | #### References 36 | 37 | * http://www.aymerick.com/2013/09/24/go_language_on_raspberrypi.html 38 | * http://www.aymerick.com/2015/10/07/influxdb-telegraf-grafana-raspberry-pi.html 39 | * http://giatro.me/2015/09/30/install-influxdb-and-grafana-on-raspberry-pi.html 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Anton Belodedenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /grafana-armhf/README.md: -------------------------------------------------------------------------------- 1 | # Grafana (armhf) 2 | 3 | ### Debian 4 | * [grafana_3.0.2_armhf.deb](https://s3.eu-central-1.amazonaws.com/belodetech/grafana_3.0.2_armhf.deb) 5 | * [grafana_3.0.4_armhf.deb](https://s3.eu-central-1.amazonaws.com/belodetech/grafana_3.0.4_armhf.deb) 6 | 7 | ### Fedora/CentOS 8 | * [grafana-3.0.2.armv7l.rpm](https://s3.eu-central-1.amazonaws.com/belodetech/grafana-3.0.2.armv7l.rpm) 9 | * [grafana-3.0.4.armv7l.rpm](https://s3.eu-central-1.amazonaws.com/belodetech/grafana-3.0.4.armv7l.rpm) 10 | 11 | ### Build 12 | Tested with `Grafana v3.0.4` on `2016-06-06`. 13 | 14 | ``` 15 | # update gcc and g++ (run once per build environment) 16 | update-alternatives --remove-all gcc 17 | update-alternatives --remove-all g++ 18 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 20 19 | update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 50 20 | update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.6 20 21 | update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.9 50 22 | update-alternatives --config gcc 23 | update-alternatives --config g++ 24 | 25 | # install phantomjs globally to v2.1.1 (run once per build environment, until dep. version changes) 26 | git clone https://github.com/shabadoo75/phantomjs-2.1.1-raspberrypi-armv7 27 | cp phantomjs-2.1.1-raspberrypi-armv7/phantomjs /usr/bin/ 28 | # make sure libicu48 is installed, https://packages.debian.org/wheezy/armhf/libicu48/download 29 | 30 | # get sources 31 | export VERSION=v3.0.4 32 | gvm use go1.5 33 | gvm pkgset create grafana 34 | gvm pkgset use grafana 35 | cd ~/.gvm/pkgsets/go1.5/grafana 36 | export GOPATH=`pwd` 37 | go get github.com/grafana/grafana || go get -u github.com/grafana/grafana 38 | cd $GOPATH/src/github.com/grafana/grafana 39 | git checkout $VERSION 40 | 41 | # build 42 | go run build.go setup 43 | $GOPATH/bin/godep restore 44 | npm install npm -g 45 | npm install 46 | npm install -g grunt-cli 47 | npm rebuild node-sass 48 | go run build.go build package 49 | ``` 50 | 51 | ### Install (Debian) 52 | 53 | ``` 54 | dpkg -i dist/*.deb 55 | ``` 56 | 57 | #### References 58 | 59 | * http://www.aymerick.com/2013/09/24/go_language_on_raspberrypi.html 60 | * http://www.aymerick.com/2015/10/07/influxdb-telegraf-grafana-raspberry-pi.html 61 | * https://hwwong168.wordpress.com/2015/11/19/building-grafana-for-raspberry-pi-2/ 62 | * http://giatro.me/2015/09/30/install-influxdb-and-grafana-on-raspberry-pi.html 63 | * https://github.com/shabadoo75/phantomjs-2.1.1-raspberrypi-armv7 64 | -------------------------------------------------------------------------------- /ups.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from influxdb import InfluxDBClient 4 | import os, time, PyNUT, argparse, socket 5 | 6 | WAIT_TIME = 60 # seconds 7 | 8 | 9 | def main(host='localhost', port=8086, ups='upsoem'): 10 | user = 'admin' 11 | password = 'admin' 12 | dbname = 'beastcraft' 13 | dbclient = InfluxDBClient(host, port, user, password, dbname) 14 | 15 | while True: 16 | try: 17 | nutclient = PyNUT.PyNUTClient(debug=True) 18 | nutstats = nutclient.GetUPSVars(ups) 19 | 20 | for k,v in nutstats.iteritems(): 21 | try: 22 | nutstats[k] = float(v) 23 | except ValueError: 24 | pass 25 | 26 | try: 27 | nutstats[k] = int(v) 28 | except ValueError: 29 | pass 30 | 31 | l = [] 32 | for measurement, value in nutstats.iteritems(): 33 | t = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) 34 | json_body = { 35 | 'measurement': measurement, 36 | 'tags': { 37 | 'ups': ups, 38 | }, 39 | 'time': t, 40 | 'fields': { 41 | 'value': value 42 | } 43 | } 44 | l.append(json_body) 45 | 46 | print("Write points: {0}".format(l)) 47 | dbclient.write_points(l) 48 | time.sleep(WAIT_TIME) 49 | 50 | except Exception, e: 51 | print '%s connecting to nut-server, retrying in %d seconds' % (repr(e), WAIT_TIME) 52 | time.sleep(WAIT_TIME) 53 | 54 | 55 | def parse_args(): 56 | parser = argparse.ArgumentParser( 57 | description='example code to play with InfluxDB') 58 | parser.add_argument('--host', type=str, required=False, default='localhost', 59 | help='hostname of InfluxDB http API') 60 | parser.add_argument('--port', type=int, required=False, default=8086, 61 | help='port of InfluxDB http API') 62 | parser.add_argument('--ups', type=str, required=False, default='upsoem', 63 | help='UPS name as defined in /etc/nut.conf') 64 | return parser.parse_args() 65 | 66 | 67 | if __name__ == '__main__': 68 | args = parse_args() 69 | main(host=args.host, port=args.port, ups=args.ups) 70 | -------------------------------------------------------------------------------- /api/api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import inspect, traceback, time 4 | from functools import wraps 5 | from subprocess import Popen, PIPE 6 | from flask import abort 7 | 8 | from application import application 9 | from config import (DEFAULT_TRIES, DEFAULT_DELAY, DEFAULT_BACKOFF, 10 | API_VERSION, WORKDIR, DEBUG) 11 | 12 | 13 | def retry(ExceptionToCheck, tries=DEFAULT_TRIES, delay=DEFAULT_DELAY, backoff=DEFAULT_BACKOFF, cdata=None): 14 | '''Retry calling the decorated function using an exponential backoff. 15 | http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ 16 | original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry 17 | :param ExceptionToCheck: the exception to check. may be a tuple of 18 | exceptions to check 19 | :type ExceptionToCheck: Exception or tuple 20 | :param tries: number of times to try (not retry) before giving up 21 | :type tries: int 22 | :param delay: initial delay between retries in seconds 23 | :type delay: int 24 | :param backoff: backoff multiplier e.g. value of 2 will double the delay 25 | each retry 26 | :type backoff: int 27 | :param logger: logger to use. If None, print 28 | :type logger: logging.Logger instance 29 | ''' 30 | def deco_retry(f): 31 | @wraps(f) 32 | def f_retry(*args, **kwargs): 33 | mtries, mdelay = tries, delay 34 | while mtries > 0: 35 | try: 36 | return f(*args, **kwargs) 37 | except ExceptionToCheck, e: 38 | print '%s, retrying in %d seconds (mtries=%d): %s' % (repr(e), mdelay, mtries, str(cdata)) 39 | if DEBUG == 1: traceback.print_exc() 40 | time.sleep(mdelay) 41 | mtries -= 1 42 | mdelay *= backoff 43 | return f(*args, **kwargs) 44 | return f_retry # true decorator 45 | return deco_retry 46 | 47 | 48 | @retry(Exception, cdata='method=%s()' % inspect.stack()[0][3]) 49 | def run_shell_cmd(cmd): 50 | p = Popen(cmd.split(' '), 51 | stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=False) 52 | output, err = p.communicate() 53 | return p.returncode, output, err 54 | 55 | 56 | @application.route('/api/v%s/mobile/' % API_VERSION, methods=['GET']) 57 | def cmd_mobile(cmd): 58 | if cmd in ['start', 'stop']: 59 | try: 60 | res = run_shell_cmd('%s/%s-mobile.sh' % (WORKDIR, cmd)) 61 | except Exception as e: 62 | print repr(e) 63 | abort(500) 64 | else: 65 | abort(404) 66 | 67 | return res[1] 68 | 69 | 70 | if __name__ == '__main__': 71 | application.run() 72 | -------------------------------------------------------------------------------- /w1_therm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse 4 | 5 | from influxdb import InfluxDBClient 6 | import os, time 7 | 8 | WAIT_TIME = 60 # seconds 9 | 10 | # ls /sys/bus/w1/devices 11 | DS18D20 = [{'name': '28-00000625bd01', 'location': 'under the bed'}, 12 | {'name': '28-00000626bee8', 'location': 'hab space'}, 13 | {'name': '28-0000062841eb', 'location': 'boiler cupboard'}, 14 | {'name': '28-00000628c173', 'location': 'electronics cupboard'}, 15 | {'name': '28-021564e193ff', 'location': 'outside'}] 16 | 17 | 18 | def main(host='localhost', port=8086): 19 | user = 'admin' 20 | password = 'admin' 21 | dbname = 'beastcraft' 22 | client = InfluxDBClient(host, port, user, password, dbname) 23 | 24 | os.system('modprobe w1-gpio') 25 | os.system('modprobe w1-therm') 26 | 27 | while True: 28 | l = [] 29 | for ts in DS18D20: 30 | temp_sensor = '/sys/bus/w1/devices/%s/w1_slave' % ts['name'] 31 | 32 | def temp_raw(): 33 | f = open(temp_sensor, 'r') 34 | lines = f.readlines() 35 | f.close() 36 | return lines 37 | 38 | def read_temp(): 39 | lines = temp_raw() 40 | while lines[0].strip()[-3:] != 'YES': 41 | time.sleep(0.2) 42 | lines = temp_raw() 43 | 44 | temp_output = lines[1].find('t=') 45 | if temp_output != -1: 46 | temp_string = lines[1].strip()[temp_output+2:] 47 | temp_c = float(temp_string) / 1000.0 48 | return temp_c 49 | 50 | temp = read_temp() 51 | print('sensor_id=%s, temp_c=%s' % (ts['name'], temp)) 52 | 53 | t = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) 54 | json_body = { 55 | "measurement": "temp", 56 | "tags": { 57 | "sensor": ts['name'], 58 | "location": ts['location'] 59 | }, 60 | "time": t, 61 | "fields": { 62 | "value": temp 63 | } 64 | } 65 | l.append(json_body) 66 | 67 | print("Write points: {0}".format(l)) 68 | client.write_points(l) 69 | time.sleep(WAIT_TIME) 70 | 71 | 72 | def parse_args(): 73 | parser = argparse.ArgumentParser( 74 | description='example code to play with InfluxDB') 75 | parser.add_argument('--host', type=str, required=False, default='localhost', 76 | help='hostname of InfluxDB http API') 77 | parser.add_argument('--port', type=int, required=False, default=8086, 78 | help='port of InfluxDB http API') 79 | return parser.parse_args() 80 | 81 | 82 | if __name__ == '__main__': 83 | args = parse_args() 84 | main(host=args.host, port=args.port) 85 | -------------------------------------------------------------------------------- /fortigate/monitor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from subprocess import Popen, PIPE 4 | from time import sleep 5 | from fcntl import fcntl, F_GETFL, F_SETFL 6 | from os import O_NONBLOCK, read 7 | import argparse 8 | 9 | 10 | def set_defaut_route(p=None, iface='wifi', gw='0.0.0.0'): 11 | print 'setting default route iface=%s gw=%s\n' % (iface, gw) 12 | p.stdin.write('config router static\nedit 1\nset device %s\nset gateway %s\nend\n' % (iface, gw)) 13 | 14 | 15 | def main(user='admin', host=None, port=22, iface=None, backup=None, gwip=None): 16 | # assume interface is online 17 | iface_status = 'alive' 18 | # run the shell as a subprocess: 19 | p = Popen(['ssh', '%s@%s' % (user, host)], 20 | stdin = PIPE, stdout = PIPE, stderr = PIPE, shell = False) 21 | # set the O_NONBLOCK flag of p.stdout file descriptor: 22 | flags = fcntl(p.stdout, F_GETFL) # get current p.stdout flags 23 | fcntl(p.stdout, F_SETFL, flags | O_NONBLOCK) 24 | # get the output 25 | while True: 26 | # issue command: 27 | p.stdin.write('diag sys link-monitor status %s\n' % iface) 28 | # let the shell output the result: 29 | sleep(5) 30 | try: 31 | line = read(p.stdout.fileno(), 1024) 32 | if line.split()[0] == 'Link': 33 | print '%s %s %s' % (line.split()[2], line.split()[3], line.split()[4]) 34 | if line.split()[4] == 'die' and iface_status == 'alive': 35 | iface_status = line.split()[4] 36 | set_defaut_route(p=p, iface=backup, gw=args.gwip) 37 | if line.split()[4] == 'alive' and iface_status == 'die': 38 | set_defaut_route(p=p, iface=iface) 39 | iface_status = line.split()[4] 40 | 41 | except OSError: 42 | # the os throws an exception if there is no data 43 | sleep(5) 44 | 45 | 46 | def parse_args(): 47 | parser = argparse.ArgumentParser(description='FortiGate interface monitor') 48 | parser.add_argument('--host', type=str, required=True, default=None, 49 | help='FortiGate appliance hostname or IP') 50 | parser.add_argument('--port', type=int, required=False, default=22, 51 | help='SSH port of the FortiGate appliance') 52 | parser.add_argument('--user', type=str, required=False, default='admin', 53 | help='FortiGate admin username') 54 | parser.add_argument('--iface', type=str, required=True, default=None, 55 | help='FortiGate interface to monitor (e.g. wifi)') 56 | parser.add_argument('--backup', type=str, required=True, default=None, 57 | help='FortiGate interface to fail-over to (e.g. wan2)') 58 | parser.add_argument('--gwip', type=str, required=True, default=None, 59 | help='Backup interface gateway ipaddr') 60 | return parser.parse_args() 61 | 62 | 63 | if __name__ == '__main__': 64 | args = parse_args() 65 | main(user=args.user, 66 | host=args.host, 67 | port=args.port, 68 | iface=args.iface, 69 | backup=args.backup, 70 | gwip=args.gwip) 71 | -------------------------------------------------------------------------------- /satinternet.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse, requests 4 | 5 | from influxdb import InfluxDBClient 6 | import os, time 7 | from pprint import pprint 8 | 9 | WAIT_TIME = 60 # seconds 10 | 11 | 12 | def merge_dicts(x, y): 13 | z = x.copy() 14 | z.update(y) 15 | return z 16 | 17 | 18 | def main(host='localhost', port=8086): 19 | user = 'admin' 20 | password = 'admin' 21 | dbname = 'beastcraft' 22 | dbclient = InfluxDBClient(host, port, user, password, dbname) 23 | 24 | host = '192.168.1.1' 25 | url = 'http://%s/cgi-bin/diagnostic_report' % host 26 | 27 | while True: 28 | try: 29 | r = requests.get(url) 30 | res = r.text 31 | 32 | l = res.split('\n')[3:12] 33 | fields = [el.split(' : ')[0].strip().replace(' ', '_') for el in l] 34 | data = [el.split(' : ')[1].strip() for el in l] 35 | d = dict(zip(fields, data)) 36 | 37 | l = res.split('\n')[16:22] 38 | fields = [el.split(' : ')[0].strip().replace(' ', '_') for el in l] 39 | data = [el.split(' : ')[1].strip() for el in l] 40 | d = merge_dicts(d, dict(zip(fields, data))) 41 | 42 | l = res.split('\n')[92:108] 43 | fields = [el.split(': ')[0].strip().replace(' ', '_') for el in l] 44 | data = [el.split(': ')[1].strip() for el in l] 45 | d = merge_dicts(d, dict(zip(fields, data))) 46 | 47 | for k,v in d.iteritems(): 48 | try: 49 | d[k] = float(v) 50 | except ValueError: 51 | pass 52 | 53 | try: 54 | d[k] = int(v) 55 | except ValueError: 56 | pass 57 | 58 | l = [] 59 | for measurement, value in d.iteritems(): 60 | t = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) 61 | if not value: continue 62 | json_body = { 63 | "measurement": measurement, 64 | "tags": { 65 | "modem": host, 66 | }, 67 | "time": t, 68 | "fields": { 69 | "value": value 70 | } 71 | } 72 | l.append(json_body) 73 | 74 | print("Write points: {0}".format(l)) 75 | dbclient.write_points(l) 76 | time.sleep(WAIT_TIME) 77 | 78 | except Exception, e: 79 | print '%s retrieving satellite modem stats, retrying in %d seconds' % (repr(e), WAIT_TIME) 80 | time.sleep(WAIT_TIME) 81 | 82 | 83 | def parse_args(): 84 | parser = argparse.ArgumentParser( 85 | description='example code to play with InfluxDB') 86 | parser.add_argument('--host', type=str, required=False, default='localhost', 87 | help='hostname of InfluxDB http API') 88 | parser.add_argument('--port', type=int, required=False, default=8086, 89 | help='port of InfluxDB http API') 90 | return parser.parse_args() 91 | 92 | 93 | if __name__ == '__main__': 94 | args = parse_args() 95 | main(host=args.host, port=args.port) 96 | -------------------------------------------------------------------------------- /mobilebroadband.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse, requests 4 | 5 | from influxdb import InfluxDBClient 6 | import os, time, json 7 | from pprint import pprint 8 | 9 | WAIT_TIME = 60 # seconds 10 | 11 | 12 | def main(host='localhost', port=8086): 13 | user = 'admin' 14 | password = 'admin' 15 | dbname = 'beastcraft' 16 | dbclient = InfluxDBClient(host, port, user, password, dbname) 17 | 18 | host = '172.17.0.1' 19 | url = 'http://%s/goform/goform_get_cmd_process' % host 20 | 21 | query_strings = [{'multi_data': 1, 22 | 'isTest': 'false', 23 | 'sms_received_flag_flag': 0, 24 | 'sts_received_flag_flag': 0, 25 | 'cmd': 'modem_main_state,pin_status,loginfo,new_version_state,current_upgrade_state,is_mandatory,sms_received_flag,sts_received_flag,signalbar,network_type,network_provider,ppp_status,EX_SSID1,ex_wifi_status,EX_wifi_profile,m_ssid_enable,sms_unread_num,RadioOff,simcard_roam,lan_ipaddr,station_mac,battery_charging,battery_vol_percent,battery_pers,spn_display_flag,plmn_display_flag,spn_name_data,spn_b1_flag,spn_b2_flag,realtime_tx_bytes,realtime_rx_bytes,realtime_time,realtime_tx_thrpt,realtime_rx_thrpt,monthly_rx_bytes,monthly_tx_bytes,monthly_time,date_month,data_volume_limit_switch,data_volume_limit_size,data_volume_alert_percent,data_volume_limit_unit,roam_setting_option,upg_roam_switch,hplmn'}, 26 | {'isTest': 'false', 27 | 'cmd': 'ConnectionMode'}] 28 | 29 | hdrs = {'Referer': 'http://%s/' % host} 30 | 31 | while True: 32 | try: 33 | for query_string in query_strings: 34 | r = requests.get(url, 35 | params=query_string, 36 | headers=hdrs) 37 | 38 | res = json.loads(r.text, strict=False) 39 | 40 | for k,v in res.iteritems(): 41 | try: 42 | res[k] = float(v) 43 | except ValueError: 44 | pass 45 | 46 | try: 47 | res[k] = int(v) 48 | except ValueError: 49 | pass 50 | 51 | l = [] 52 | for measurement, value in res.iteritems(): 53 | t = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) 54 | if not value: continue 55 | json_body = { 56 | "measurement": measurement, 57 | "tags": { 58 | "modem": host, 59 | }, 60 | "time": t, 61 | "fields": { 62 | "value": value 63 | } 64 | } 65 | l.append(json_body) 66 | 67 | print("Write points: {0}".format(l)) 68 | dbclient.write_points(l) 69 | time.sleep(WAIT_TIME) 70 | 71 | except Exception, e: 72 | print '%s retrieving modem stats, retrying in %d seconds' % (repr(e), WAIT_TIME) 73 | time.sleep(WAIT_TIME) 74 | 75 | 76 | def parse_args(): 77 | parser = argparse.ArgumentParser( 78 | description='example code to play with InfluxDB') 79 | parser.add_argument('--host', type=str, required=False, default='localhost', 80 | help='hostname of InfluxDB http API') 81 | parser.add_argument('--port', type=int, required=False, default=8086, 82 | help='port of InfluxDB http API') 83 | return parser.parse_args() 84 | 85 | 86 | if __name__ == '__main__': 87 | args = parse_args() 88 | main(host=args.host, port=args.port) 89 | -------------------------------------------------------------------------------- /geo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from influxdb import InfluxDBClient 4 | import sys, os, time, json, gps, argparse, requests, calendar 5 | from pprint import pprint 6 | from datetime import datetime 7 | import numpy as np 8 | import dns.query 9 | import dns.tsigkeyring 10 | import dns.update 11 | import dns.rdatatype 12 | 13 | WAIT_TIME = 60 # seconds 14 | 15 | 16 | def main(host='localhost', port=8086, domain=None, key=None): 17 | try: 18 | user = 'admin' 19 | password = 'admin' 20 | dbname = 'beastcraft' 21 | dbclient = InfluxDBClient(host, port, user, password, dbname) 22 | 23 | session = gps.gps(host='localhost', port='2947') 24 | session.stream(gps.WATCH_ENABLE|gps.WATCH_NEWSTYLE) 25 | start_time = time.time() - WAIT_TIME 26 | reports = [] 27 | for report in session: 28 | report = report.__dict__ 29 | if report['class'] == 'TPV': 30 | reports.append(report) 31 | if time.time() - start_time > WAIT_TIME: 32 | write_db(dbclient, summarise_rpt(reports), domain=domain, key=key) 33 | reports = [] 34 | start_time = time.time() 35 | 36 | except Exception, e: 37 | print '%s retrieving GPS stats, retrying in %d seconds' % (repr(e), WAIT_TIME) 38 | time.sleep(WAIT_TIME) 39 | 40 | 41 | def average_val(rpts, name): 42 | l = [rpt[name] for rpt in rpts] 43 | return reduce(lambda x, y: x + y, l) / len(l) 44 | 45 | 46 | def median_val(rpts, name): 47 | l = [rpt[name] for rpt in rpts] 48 | return np.percentile(l, 50) 49 | 50 | 51 | def summarise_rpt(rpts): 52 | report = dict() 53 | report['lat'] = median_val(rpts, 'lat') 54 | report['lon'] = median_val(rpts, 'lon') 55 | report['epx'] = median_val(rpts, 'epx') 56 | report['epy'] = median_val(rpts, 'epy') 57 | report['epv'] = median_val(rpts, 'epv') 58 | report['ept'] = median_val(rpts, 'ept') 59 | report['eps'] = median_val(rpts, 'eps') 60 | report['climb'] = median_val(rpts, 'climb') 61 | report['alt'] = median_val(rpts, 'alt') 62 | report['speed'] = median_val(rpts, 'speed') 63 | report['time'] = [rpt['time'] for rpt in rpts][-1] 64 | report['device'] = [rpt['device'] for rpt in rpts][-1] 65 | report['class'] = [rpt['class'] for rpt in rpts][-1] 66 | report['mode'] = [rpt['mode'] for rpt in rpts][-1] 67 | report['track'] = [rpt['track'] for rpt in rpts][-1] 68 | report['geo'] = '%s,%s' % (report['lat'], report['lon']) 69 | report['epoch'] = calendar.timegm(datetime.strptime(report['time'], 70 | '%Y-%m-%dT%H:%M:%S.%fZ').timetuple()) 71 | 72 | return report 73 | 74 | 75 | def write_db(dbc, rpt, domain=None, key=None): 76 | l = [] 77 | for measurement, value in rpt.iteritems(): 78 | t = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) 79 | if measurement not in ['class', 'device']: 80 | json_body = { 81 | 'measurement': measurement, 82 | 'tags': { 83 | 'class': rpt['class'], 84 | 'device': rpt['device'] 85 | }, 86 | 'time': t, 87 | 'fields': { 88 | 'value': value 89 | } 90 | } 91 | l.append(json_body) 92 | 93 | print('Write points: {0}'.format(l)) 94 | dbc.write_points(l) 95 | update_dns(coords=rpt['geo'], domain=domain, key=key) 96 | 97 | 98 | def update_dns(coords=None, domain=None, key=None): 99 | if domain and key and coords: 100 | print('Update DNS: {0}'.format(coords)) 101 | keyring = dns.tsigkeyring.from_text({ 102 | domain : key 103 | }) 104 | 105 | update = dns.update.Update(domain, keyring=keyring) 106 | update.replace('geo', 300, dns.rdatatype.TXT, '"%s"' % coords) 107 | 108 | return dns.query.tcp(update, 'localhost') 109 | else: 110 | return None 111 | 112 | 113 | def parse_args(): 114 | parser = argparse.ArgumentParser(description='GPSd InfluxDB loader') 115 | parser.add_argument('--host', type=str, required=False, default='localhost', 116 | help='hostname of InfluxDB http API') 117 | parser.add_argument('--port', type=int, required=False, default=8086, 118 | help='port of InfluxDB http API') 119 | parser.add_argument('--domain', type=str, required=False, default=None, 120 | help='DNS domain to update with geo TXT record') 121 | parser.add_argument('--key', type=str, required=False, default=None, 122 | help='DNSSEC key') 123 | return parser.parse_args() 124 | 125 | 126 | if __name__ == '__main__': 127 | args = parse_args() 128 | main(host=args.host, 129 | port=args.port, 130 | domain=args.domain, 131 | key=args.key) 132 | -------------------------------------------------------------------------------- /traffic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from subprocess import Popen, PIPE 4 | from influxdb import InfluxDBClient 5 | import os, time, argparse 6 | 7 | def main(cmd=None, host=None, port=None): 8 | user = 'admin' 9 | password = 'admin' 10 | dbname = 'beastcraft' 11 | client = InfluxDBClient(host, port, user, password, dbname) 12 | 13 | try: 14 | p = Popen(cmd, stdout=PIPE) 15 | lines_iterator = iter(p.stdout.readline, b'') 16 | for line in lines_iterator: 17 | l = line.replace('\n', '').strip().split(',') 18 | t = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) 19 | 20 | fields = ['sample', 'agent'] 21 | 22 | if l[0] in 'FLOW': 23 | names = ['inputPort', 24 | 'outputPort', 25 | 'src_MAC', 26 | 'dst_MAC', 27 | 'ethernet_type', 28 | 'in_vlan', 29 | 'out_vlan', 30 | 'src_IP', 31 | 'dst_IP', 32 | 'IP_protocol', 33 | 'ip_tos', 34 | 'ip_ttl', 35 | 'tcp_udp_src_port_icmp_code', 36 | 'tcp_udp_dst_port_icmp_code', 37 | 'tcp_flags', 38 | 'packet_size', 39 | 'IP_size', 40 | 'sampling_rate'] 41 | 42 | print 'SFLOW not currently supported' 43 | 44 | if l[0] in 'CNTR': 45 | names = ['ifIndex', 46 | 'ifType', 47 | 'ifSpeed', 48 | 'ifDirection', 49 | 'ifStatus', 50 | 'ifInOctets', 51 | 'ifInUcastPkts', 52 | 'ifInMulticastPkts', 53 | 'ifInBroadcastPkts', 54 | 'ifInDiscards', 55 | 'ifInErrors', 56 | 'ifInUnknownProtos', 57 | 'ifOutOctets', 58 | 'ifOutUcastPkts', 59 | 'ifOutMulticastPkts', 60 | 'ifOutBroadcastPkts', 61 | 'ifOutDiscards', 62 | 'ifOutErrors', 63 | 'ifPromiscuousMode'] 64 | 65 | for name in names: fields.append(name) 66 | d = dict(zip(fields, l)) 67 | 68 | l = [] 69 | for k in ['ifInOctets', 'ifInUcastPkts', 'ifInMulticastPkts', 70 | 'ifInBroadcastPkts', 'ifInDiscards', 'ifInErrors', 71 | 'ifInUnknownProtos', 'ifOutOctets', 'ifOutUcastPkts', 72 | 'ifOutMulticastPkts', 'ifOutBroadcastPkts', 'ifOutDiscards', 73 | 'ifOutErrors']: 74 | 75 | json_body = { 76 | 'measurement': k, 77 | 'tags': { 78 | 'sample': d['sample'], 79 | 'agent': d['agent'], 80 | 'ifIndex': int(d['ifIndex']), 81 | 'ifType': int(d['ifType']), 82 | 'ifSpeed': int(d['ifSpeed']), 83 | 'ifDirection': int(d['ifDirection']), 84 | 'ifStatus': int(d['ifStatus']), 85 | 'ifPromiscuousMode': int(d['ifPromiscuousMode']) 86 | }, 87 | 'time': t, 88 | 'fields': { 89 | 'value': int(d[k]) 90 | } 91 | } 92 | l.append(json_body) 93 | 94 | print('Write points: {0}'.format(l)) 95 | client.write_points(l) 96 | 97 | except Exception as e: 98 | print str(e) 99 | p.kill() 100 | cleanup() 101 | 102 | 103 | def cleanup(): 104 | p = Popen(['killall', 'sflowtool'], stdout=PIPE) 105 | p.wait() 106 | 107 | 108 | def parse_args(): 109 | parser = argparse.ArgumentParser( 110 | description='example code to play with InfluxDB') 111 | parser.add_argument('--host', type=str, required=False, default='localhost', 112 | help='hostname of InfluxDB http API') 113 | parser.add_argument('--port', type=int, required=False, default=8086, 114 | help='port of InfluxDB http API') 115 | parser.add_argument('--sflowcmd', type=str, required=False, default=['/usr/local/bin/sflowtool', '-4', '-p', '6343', '-l'], 116 | help='sflowtool command') 117 | return parser.parse_args() 118 | 119 | 120 | if __name__ == '__main__': 121 | args = parse_args() 122 | cleanup() 123 | main(cmd=args.sflowcmd, host=args.host, port=args.port) 124 | -------------------------------------------------------------------------------- /PyNUT/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (C) 2008 David Goncalves 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | # 2008-01-14 David Goncalves 20 | # PyNUT is an abstraction class to access NUT (Network UPS Tools) server. 21 | # 22 | # 2008-06-09 David Goncalves 23 | # Added 'GetRWVars' and 'SetRWVar' commands. 24 | # 25 | # 2009-02-19 David Goncalves 26 | # Changed class PyNUT to PyNUTClient 27 | # 28 | # 2010-07-23 David Goncalves - Version 1.2 29 | # Changed GetRWVars function that fails is the UPS is not 30 | # providing such vars. 31 | # 32 | # 2011-07-05 René Martín Rodríguez - Version 1.2.1 33 | # Added support for FSD, HELP and VER commands 34 | # 35 | # 2012-02-07 René Martín Rodríguez - Version 1.2.2 36 | # Added support for LIST CLIENTS command 37 | # 38 | # 2014-06-03 george2 - Version 1.3.0 39 | # Added custom exception class, fixed minor bug, added Python 3 support. 40 | # 41 | 42 | import telnetlib 43 | 44 | class PyNUTError( Exception ) : 45 | """ Base class for custom exceptions """ 46 | 47 | 48 | class PyNUTClient : 49 | """ Abstraction class to access NUT (Network UPS Tools) server """ 50 | 51 | __debug = None # Set class to debug mode (prints everything useful for debuging...) 52 | __host = None 53 | __port = None 54 | __login = None 55 | __password = None 56 | __timeout = None 57 | __srv_handler = None 58 | 59 | __version = "1.3.0" 60 | __release = "2014-06-03" 61 | 62 | 63 | def __init__( self, host="127.0.0.1", port=3493, login=None, password=None, debug=False, timeout=5 ) : 64 | """ Class initialization method 65 | 66 | host : Host to connect (default to localhost) 67 | port : Port where NUT listens for connections (default to 3493) 68 | login : Login used to connect to NUT server (default to None for no authentication) 69 | password : Password used when using authentication (default to None) 70 | debug : Boolean, put class in debug mode (prints everything on console, default to False) 71 | timeout : Timeout used to wait for network response 72 | """ 73 | self.__debug = debug 74 | 75 | if self.__debug : 76 | print( "[DEBUG] Class initialization..." ) 77 | print( "[DEBUG] -> Host = %s (port %s)" % ( host, port ) ) 78 | print( "[DEBUG] -> Login = '%s' / '%s'" % ( login, password ) ) 79 | 80 | self.__host = host 81 | self.__port = port 82 | self.__login = login 83 | self.__password = password 84 | self.__timeout = 5 85 | 86 | self.__connect() 87 | 88 | # Try to disconnect cleanly when class is deleted ;) 89 | def __del__( self ) : 90 | """ Class destructor method """ 91 | try : 92 | self.__srv_handler.write( "LOGOUT\n" ) 93 | except : 94 | pass 95 | 96 | def __connect( self ) : 97 | """ Connects to the defined server 98 | 99 | If login/pass was specified, the class tries to authenticate. An error is raised 100 | if something goes wrong. 101 | """ 102 | if self.__debug : 103 | print( "[DEBUG] Connecting to host" ) 104 | 105 | self.__srv_handler = telnetlib.Telnet( self.__host, self.__port ) 106 | 107 | if self.__login != None : 108 | self.__srv_handler.write( "USERNAME %s\n" % self.__login ) 109 | result = self.__srv_handler.read_until( "\n", self.__timeout ) 110 | if result[:2] != "OK" : 111 | raise PyNUTError( result.replace( "\n", "" ) ) 112 | 113 | if self.__password != None : 114 | self.__srv_handler.write( "PASSWORD %s\n" % self.__password ) 115 | result = self.__srv_handler.read_until( "\n", self.__timeout ) 116 | if result[:2] != "OK" : 117 | raise PyNUTError( result.replace( "\n", "" ) ) 118 | 119 | def GetUPSList( self ) : 120 | """ Returns the list of available UPS from the NUT server 121 | 122 | The result is a dictionary containing 'key->val' pairs of 'UPSName' and 'UPS Description' 123 | """ 124 | if self.__debug : 125 | print( "[DEBUG] GetUPSList from server" ) 126 | 127 | self.__srv_handler.write( "LIST UPS\n" ) 128 | result = self.__srv_handler.read_until( "\n" ) 129 | if result != "BEGIN LIST UPS\n" : 130 | raise PyNUTError( result.replace( "\n", "" ) ) 131 | 132 | result = self.__srv_handler.read_until( "END LIST UPS\n" ) 133 | ups_list = {} 134 | 135 | for line in result.split( "\n" ) : 136 | if line[:3] == "UPS" : 137 | ups, desc = line[4:-1].split( '"' ) 138 | ups_list[ ups.replace( " ", "" ) ] = desc 139 | 140 | return( ups_list ) 141 | 142 | def GetUPSVars( self, ups="" ) : 143 | """ Get all available vars from the specified UPS 144 | 145 | The result is a dictionary containing 'key->val' pairs of all 146 | available vars. 147 | """ 148 | if self.__debug : 149 | print( "[DEBUG] GetUPSVars called..." ) 150 | 151 | self.__srv_handler.write( "LIST VAR %s\n" % ups ) 152 | result = self.__srv_handler.read_until( "\n" ) 153 | if result != "BEGIN LIST VAR %s\n" % ups : 154 | raise PyNUTError( result.replace( "\n", "" ) ) 155 | 156 | ups_vars = {} 157 | result = self.__srv_handler.read_until( "END LIST VAR %s\n" % ups ) 158 | offset = len( "VAR %s " % ups ) 159 | end_offset = 0 - ( len( "END LIST VAR %s\n" % ups ) + 1 ) 160 | 161 | for current in result[:end_offset].split( "\n" ) : 162 | var = current[ offset: ].split( '"' )[0].replace( " ", "" ) 163 | data = current[ offset: ].split( '"' )[1] 164 | ups_vars[ var ] = data 165 | 166 | return( ups_vars ) 167 | 168 | def GetUPSCommands( self, ups="" ) : 169 | """ Get all available commands for the specified UPS 170 | 171 | The result is a dict object with command name as key and a description 172 | of the command as value 173 | """ 174 | if self.__debug : 175 | print( "[DEBUG] GetUPSCommands called..." ) 176 | 177 | self.__srv_handler.write( "LIST CMD %s\n" % ups ) 178 | result = self.__srv_handler.read_until( "\n" ) 179 | if result != "BEGIN LIST CMD %s\n" % ups : 180 | raise PyNUTError( result.replace( "\n", "" ) ) 181 | 182 | ups_cmds = {} 183 | result = self.__srv_handler.read_until( "END LIST CMD %s\n" % ups ) 184 | offset = len( "CMD %s " % ups ) 185 | end_offset = 0 - ( len( "END LIST CMD %s\n" % ups ) + 1 ) 186 | 187 | for current in result[:end_offset].split( "\n" ) : 188 | var = current[ offset: ].split( '"' )[0].replace( " ", "" ) 189 | 190 | # For each var we try to get the available description 191 | try : 192 | self.__srv_handler.write( "GET CMDDESC %s %s\n" % ( ups, var ) ) 193 | temp = self.__srv_handler.read_until( "\n" ) 194 | if temp[:7] != "CMDDESC" : 195 | raise PyNUTError 196 | else : 197 | off = len( "CMDDESC %s %s " % ( ups, var ) ) 198 | desc = temp[off:-1].split('"')[1] 199 | except : 200 | desc = var 201 | 202 | ups_cmds[ var ] = desc 203 | 204 | return( ups_cmds ) 205 | 206 | def GetRWVars( self, ups="" ) : 207 | """ Get a list of all writable vars from the selected UPS 208 | 209 | The result is presented as a dictionary containing 'key->val' pairs 210 | """ 211 | if self.__debug : 212 | print( "[DEBUG] GetUPSVars from '%s'..." % ups ) 213 | 214 | self.__srv_handler.write( "LIST RW %s\n" % ups ) 215 | result = self.__srv_handler.read_until( "\n" ) 216 | if ( result != "BEGIN LIST RW %s\n" % ups ) : 217 | raise PyNUTError( result.replace( "\n", "" ) ) 218 | 219 | result = self.__srv_handler.read_until( "END LIST RW %s\n" % ups ) 220 | offset = len( "VAR %s" % ups ) 221 | end_offset = 0 - ( len( "END LIST RW %s\n" % ups ) + 1 ) 222 | rw_vars = {} 223 | 224 | try : 225 | for current in result[:end_offset].split( "\n" ) : 226 | var = current[ offset: ].split( '"' )[0].replace( " ", "" ) 227 | data = current[ offset: ].split( '"' )[1] 228 | rw_vars[ var ] = data 229 | 230 | except : 231 | pass 232 | 233 | return( rw_vars ) 234 | 235 | def SetRWVar( self, ups="", var="", value="" ): 236 | """ Set a variable to the specified value on selected UPS 237 | 238 | The variable must be a writable value (cf GetRWVars) and you must have the proper 239 | rights to set it (maybe login/password). 240 | """ 241 | 242 | self.__srv_handler.write( "SET VAR %s %s %s\n" % ( ups, var, value ) ) 243 | result = self.__srv_handler.read_until( "\n" ) 244 | if ( result == "OK\n" ) : 245 | return( "OK" ) 246 | else : 247 | raise PyNUTError( result ) 248 | 249 | def RunUPSCommand( self, ups="", command="" ) : 250 | """ Send a command to the specified UPS 251 | 252 | Returns OK on success or raises an error 253 | """ 254 | 255 | if self.__debug : 256 | print( "[DEBUG] RunUPSCommand called..." ) 257 | 258 | self.__srv_handler.write( "INSTCMD %s %s\n" % ( ups, command ) ) 259 | result = self.__srv_handler.read_until( "\n" ) 260 | if ( result == "OK\n" ) : 261 | return( "OK" ) 262 | else : 263 | raise PyNUTError( result.replace( "\n", "" ) ) 264 | 265 | def FSD( self, ups="") : 266 | """ Send FSD command 267 | 268 | Returns OK on success or raises an error 269 | """ 270 | 271 | if self.__debug : 272 | print( "[DEBUG] MASTER called..." ) 273 | 274 | self.__srv_handler.write( "MASTER %s\n" % ups ) 275 | result = self.__srv_handler.read_until( "\n" ) 276 | if ( result != "OK MASTER-GRANTED\n" ) : 277 | raise PyNUTError( ( "Master level function are not available", "" ) ) 278 | 279 | if self.__debug : 280 | print( "[DEBUG] FSD called..." ) 281 | self.__srv_handler.write( "FSD %s\n" % ups ) 282 | result = self.__srv_handler.read_until( "\n" ) 283 | if ( result == "OK FSD-SET\n" ) : 284 | return( "OK" ) 285 | else : 286 | raise PyNUTError( result.replace( "\n", "" ) ) 287 | 288 | def help(self) : 289 | """ Send HELP command 290 | """ 291 | 292 | if self.__debug : 293 | print( "[DEBUG] HELP called..." ) 294 | 295 | self.__srv_handler.write( "HELP\n") 296 | return self.__srv_handler.read_until( "\n" ) 297 | 298 | def ver(self) : 299 | """ Send VER command 300 | """ 301 | 302 | if self.__debug : 303 | print( "[DEBUG] VER called..." ) 304 | 305 | self.__srv_handler.write( "VER\n") 306 | return self.__srv_handler.read_until( "\n" ) 307 | 308 | def ListClients( self, ups = None ) : 309 | """ Returns the list of connected clients from the NUT server 310 | 311 | The result is a dictionary containing 'key->val' pairs of 'UPSName' and a list of clients 312 | """ 313 | if self.__debug : 314 | print( "[DEBUG] ListClients from server" ) 315 | 316 | if ups and (ups not in self.GetUPSList()): 317 | raise PyNUTError( "%s is not a valid UPS" % ups ) 318 | 319 | if ups: 320 | self.__srv_handler.write( "LIST CLIENTS %s\n" % ups) 321 | else: 322 | self.__srv_handler.write( "LIST CLIENTS\n" ) 323 | result = self.__srv_handler.read_until( "\n" ) 324 | if result != "BEGIN LIST CLIENTS\n" : 325 | raise PyNUTError( result.replace( "\n", "" ) ) 326 | 327 | result = self.__srv_handler.read_until( "END LIST CLIENTS\n" ) 328 | ups_list = {} 329 | 330 | for line in result.split( "\n" ): 331 | if line[:6] == "CLIENT" : 332 | host, ups = line[7:].split(' ') 333 | ups.replace(' ', '') 334 | if not ups in ups_list: 335 | ups_list[ups] = [] 336 | ups_list[ups].append(host) 337 | 338 | return( ups_list ) 339 | -------------------------------------------------------------------------------- /temp.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "title": "Temperature", 4 | "originalTitle": "Temperature", 5 | "tags": [ 6 | "temp" 7 | ], 8 | "style": "dark", 9 | "timezone": "browser", 10 | "editable": true, 11 | "hideControls": false, 12 | "sharedCrosshair": false, 13 | "rows": [ 14 | { 15 | "collapse": false, 16 | "editable": true, 17 | "height": "0", 18 | "panels": [ 19 | { 20 | "cacheTimeout": null, 21 | "colorBackground": false, 22 | "colorValue": true, 23 | "colors": [ 24 | "rgba(245, 54, 54, 0.9)", 25 | "rgba(237, 129, 40, 0.89)", 26 | "rgba(50, 172, 45, 0.97)" 27 | ], 28 | "datasource": "BeastCraft", 29 | "decimals": 2, 30 | "editable": true, 31 | "error": false, 32 | "format": "celsius", 33 | "gauge": { 34 | "maxValue": 100, 35 | "minValue": 0, 36 | "show": false, 37 | "thresholdLabels": false, 38 | "thresholdMarkers": true 39 | }, 40 | "height": "100px", 41 | "id": 7, 42 | "interval": null, 43 | "isNew": true, 44 | "links": [], 45 | "maxDataPoints": 100, 46 | "nullPointMode": "connected", 47 | "nullText": null, 48 | "postfix": "", 49 | "postfixFontSize": "50%", 50 | "prefix": "", 51 | "prefixFontSize": "50%", 52 | "span": 3, 53 | "sparkline": { 54 | "fillColor": "rgba(31, 118, 189, 0.18)", 55 | "full": true, 56 | "lineColor": "rgb(31, 120, 193)", 57 | "show": true 58 | }, 59 | "targets": [ 60 | { 61 | "dsType": "influxdb", 62 | "groupBy": [], 63 | "measurement": "temp", 64 | "policy": "default", 65 | "query": "SELECT \"value\" FROM \"temp\" WHERE \"sensor\" = '28-021564e193ff' AND $timeFilter", 66 | "rawQuery": true, 67 | "refId": "A", 68 | "resultFormat": "time_series", 69 | "select": [ 70 | [ 71 | { 72 | "params": [ 73 | "value" 74 | ], 75 | "type": "field" 76 | } 77 | ] 78 | ], 79 | "tags": [ 80 | { 81 | "key": "sensor", 82 | "operator": "=", 83 | "value": "28-021564e193ff" 84 | } 85 | ] 86 | } 87 | ], 88 | "thresholds": "16,25,30", 89 | "timeFrom": null, 90 | "timeShift": null, 91 | "title": "outside", 92 | "transparent": true, 93 | "type": "singlestat", 94 | "valueFontSize": "80%", 95 | "valueMaps": [ 96 | { 97 | "op": "=", 98 | "text": "N/A", 99 | "value": "null" 100 | } 101 | ], 102 | "valueName": "current" 103 | }, 104 | { 105 | "cacheTimeout": null, 106 | "colorBackground": false, 107 | "colorValue": false, 108 | "colors": [ 109 | "rgba(245, 54, 54, 0.9)", 110 | "rgba(237, 129, 40, 0.89)", 111 | "rgba(50, 172, 45, 0.97)" 112 | ], 113 | "datasource": "BeastCraft", 114 | "decimals": 2, 115 | "editable": true, 116 | "error": false, 117 | "format": "celsius", 118 | "gauge": { 119 | "maxValue": 100, 120 | "minValue": 0, 121 | "show": false, 122 | "thresholdLabels": false, 123 | "thresholdMarkers": true 124 | }, 125 | "height": "100px", 126 | "id": 5, 127 | "interval": null, 128 | "isNew": true, 129 | "links": [], 130 | "maxDataPoints": 100, 131 | "nullPointMode": "connected", 132 | "nullText": null, 133 | "postfix": "", 134 | "postfixFontSize": "50%", 135 | "prefix": "", 136 | "prefixFontSize": "50%", 137 | "span": 2, 138 | "sparkline": { 139 | "fillColor": "rgba(31, 118, 189, 0.18)", 140 | "full": true, 141 | "lineColor": "rgb(31, 120, 193)", 142 | "show": true 143 | }, 144 | "targets": [ 145 | { 146 | "dsType": "influxdb", 147 | "groupBy": [], 148 | "measurement": "temp", 149 | "policy": "default", 150 | "query": "SELECT \"value\" FROM \"temp\" WHERE \"sensor\" = '28-0000062841eb' AND $timeFilter", 151 | "rawQuery": true, 152 | "refId": "A", 153 | "resultFormat": "time_series", 154 | "select": [ 155 | [ 156 | { 157 | "params": [ 158 | "value" 159 | ], 160 | "type": "field" 161 | } 162 | ] 163 | ], 164 | "tags": [ 165 | { 166 | "key": "sensor", 167 | "operator": "=", 168 | "value": "28-0000062841eb" 169 | } 170 | ] 171 | } 172 | ], 173 | "thresholds": "", 174 | "title": "boiler cupboard", 175 | "transparent": true, 176 | "type": "singlestat", 177 | "valueFontSize": "50%", 178 | "valueMaps": [ 179 | { 180 | "op": "=", 181 | "text": "N/A", 182 | "value": "null" 183 | } 184 | ], 185 | "valueName": "current" 186 | }, 187 | { 188 | "cacheTimeout": null, 189 | "colorBackground": false, 190 | "colorValue": false, 191 | "colors": [ 192 | "rgba(245, 54, 54, 0.9)", 193 | "rgba(237, 129, 40, 0.89)", 194 | "rgba(50, 172, 45, 0.97)" 195 | ], 196 | "datasource": "BeastCraft", 197 | "decimals": 2, 198 | "editable": true, 199 | "error": false, 200 | "format": "celsius", 201 | "gauge": { 202 | "maxValue": 100, 203 | "minValue": 0, 204 | "show": false, 205 | "thresholdLabels": false, 206 | "thresholdMarkers": true 207 | }, 208 | "height": "100px", 209 | "id": 2, 210 | "interval": null, 211 | "isNew": true, 212 | "links": [], 213 | "maxDataPoints": 100, 214 | "nullPointMode": "connected", 215 | "nullText": null, 216 | "postfix": "", 217 | "postfixFontSize": "50%", 218 | "prefix": "", 219 | "prefixFontSize": "50%", 220 | "span": 2, 221 | "sparkline": { 222 | "fillColor": "rgba(31, 118, 189, 0.18)", 223 | "full": true, 224 | "lineColor": "rgb(31, 120, 193)", 225 | "show": true 226 | }, 227 | "targets": [ 228 | { 229 | "dsType": "influxdb", 230 | "groupBy": [], 231 | "measurement": "temp", 232 | "policy": "default", 233 | "query": "SELECT \"value\" FROM \"temp\" WHERE \"sensor\" = '28-00000625bd01' AND $timeFilter", 234 | "rawQuery": true, 235 | "refId": "A", 236 | "resultFormat": "time_series", 237 | "select": [ 238 | [ 239 | { 240 | "params": [ 241 | "value" 242 | ], 243 | "type": "field" 244 | } 245 | ] 246 | ], 247 | "tags": [ 248 | { 249 | "key": "sensor", 250 | "operator": "=", 251 | "value": "28-00000625bd01" 252 | } 253 | ] 254 | } 255 | ], 256 | "thresholds": "", 257 | "title": "under the bed", 258 | "transparent": true, 259 | "type": "singlestat", 260 | "valueFontSize": "50%", 261 | "valueMaps": [ 262 | { 263 | "op": "=", 264 | "text": "N/A", 265 | "value": "null" 266 | } 267 | ], 268 | "valueName": "current" 269 | }, 270 | { 271 | "cacheTimeout": null, 272 | "colorBackground": false, 273 | "colorValue": false, 274 | "colors": [ 275 | "rgba(245, 54, 54, 0.9)", 276 | "rgba(237, 129, 40, 0.89)", 277 | "rgba(50, 172, 45, 0.97)" 278 | ], 279 | "datasource": "BeastCraft", 280 | "decimals": 2, 281 | "editable": true, 282 | "error": false, 283 | "format": "celsius", 284 | "gauge": { 285 | "maxValue": 100, 286 | "minValue": 0, 287 | "show": false, 288 | "thresholdLabels": false, 289 | "thresholdMarkers": true 290 | }, 291 | "height": "100px", 292 | "id": 6, 293 | "interval": null, 294 | "isNew": true, 295 | "links": [], 296 | "maxDataPoints": 100, 297 | "nullPointMode": "connected", 298 | "nullText": null, 299 | "postfix": "", 300 | "postfixFontSize": "50%", 301 | "prefix": "", 302 | "prefixFontSize": "50%", 303 | "span": 2, 304 | "sparkline": { 305 | "fillColor": "rgba(31, 118, 189, 0.18)", 306 | "full": true, 307 | "lineColor": "rgb(31, 120, 193)", 308 | "show": true 309 | }, 310 | "targets": [ 311 | { 312 | "dsType": "influxdb", 313 | "groupBy": [], 314 | "measurement": "temp", 315 | "policy": "default", 316 | "query": "SELECT \"value\" FROM \"temp\" WHERE \"sensor\" = '28-00000628c173' AND $timeFilter", 317 | "rawQuery": true, 318 | "refId": "A", 319 | "resultFormat": "time_series", 320 | "select": [ 321 | [ 322 | { 323 | "params": [ 324 | "value" 325 | ], 326 | "type": "field" 327 | } 328 | ] 329 | ], 330 | "tags": [ 331 | { 332 | "key": "sensor", 333 | "operator": "=", 334 | "value": "28-00000628c173" 335 | } 336 | ] 337 | } 338 | ], 339 | "thresholds": "", 340 | "title": "electronics cupboard", 341 | "transparent": true, 342 | "type": "singlestat", 343 | "valueFontSize": "50%", 344 | "valueMaps": [ 345 | { 346 | "op": "=", 347 | "text": "N/A", 348 | "value": "null" 349 | } 350 | ], 351 | "valueName": "current" 352 | }, 353 | { 354 | "cacheTimeout": null, 355 | "colorBackground": false, 356 | "colorValue": true, 357 | "colors": [ 358 | "rgba(245, 54, 54, 0.9)", 359 | "rgba(237, 129, 40, 0.89)", 360 | "rgba(50, 172, 45, 0.97)" 361 | ], 362 | "datasource": "BeastCraft", 363 | "decimals": 2, 364 | "editable": true, 365 | "error": false, 366 | "format": "celsius", 367 | "gauge": { 368 | "maxValue": 100, 369 | "minValue": 0, 370 | "show": false, 371 | "thresholdLabels": false, 372 | "thresholdMarkers": true 373 | }, 374 | "height": "100px", 375 | "id": 4, 376 | "interval": null, 377 | "isNew": true, 378 | "links": [], 379 | "maxDataPoints": 100, 380 | "nullPointMode": "connected", 381 | "nullText": null, 382 | "postfix": "", 383 | "postfixFontSize": "50%", 384 | "prefix": "", 385 | "prefixFontSize": "50%", 386 | "span": 3, 387 | "sparkline": { 388 | "fillColor": "rgba(31, 118, 189, 0.18)", 389 | "full": true, 390 | "lineColor": "rgb(31, 120, 193)", 391 | "show": true 392 | }, 393 | "targets": [ 394 | { 395 | "dsType": "influxdb", 396 | "groupBy": [], 397 | "measurement": "temp", 398 | "policy": "default", 399 | "query": "SELECT \"value\" FROM \"temp\" WHERE \"sensor\" = '28-00000626bee8' AND $timeFilter", 400 | "refId": "A", 401 | "resultFormat": "time_series", 402 | "select": [ 403 | [ 404 | { 405 | "params": [ 406 | "value" 407 | ], 408 | "type": "field" 409 | } 410 | ] 411 | ], 412 | "tags": [ 413 | { 414 | "key": "sensor", 415 | "operator": "=", 416 | "value": "28-00000626bee8" 417 | } 418 | ] 419 | } 420 | ], 421 | "thresholds": "17,25,30", 422 | "title": "hab space", 423 | "transparent": true, 424 | "type": "singlestat", 425 | "valueFontSize": "80%", 426 | "valueMaps": [ 427 | { 428 | "op": "=", 429 | "text": "N/A", 430 | "value": "null" 431 | } 432 | ], 433 | "valueName": "current" 434 | } 435 | ], 436 | "title": "Row" 437 | }, 438 | { 439 | "collapse": false, 440 | "editable": true, 441 | "height": "250px", 442 | "panels": [ 443 | { 444 | "aliasColors": {}, 445 | "bars": false, 446 | "datasource": "BeastCraft", 447 | "decimals": 3, 448 | "editable": true, 449 | "error": false, 450 | "fill": 1, 451 | "grid": { 452 | "threshold1": null, 453 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 454 | "threshold2": null, 455 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 456 | }, 457 | "height": "", 458 | "id": 1, 459 | "interval": "", 460 | "isNew": true, 461 | "legend": { 462 | "alignAsTable": false, 463 | "avg": false, 464 | "current": false, 465 | "max": false, 466 | "min": false, 467 | "rightSide": false, 468 | "show": true, 469 | "total": false, 470 | "values": false 471 | }, 472 | "lines": true, 473 | "linewidth": 2, 474 | "links": [], 475 | "nullPointMode": "connected", 476 | "percentage": false, 477 | "pointradius": 5, 478 | "points": false, 479 | "renderer": "flot", 480 | "seriesOverrides": [ 481 | { 482 | "alias": "temp", 483 | "yaxis": 1 484 | } 485 | ], 486 | "span": 12, 487 | "stack": false, 488 | "steppedLine": false, 489 | "targets": [ 490 | { 491 | "alias": "", 492 | "dsType": "influxdb", 493 | "groupBy": [ 494 | { 495 | "params": [ 496 | "$interval" 497 | ], 498 | "type": "time" 499 | }, 500 | { 501 | "params": [ 502 | "location" 503 | ], 504 | "type": "tag" 505 | }, 506 | { 507 | "params": [ 508 | "previous" 509 | ], 510 | "type": "fill" 511 | } 512 | ], 513 | "measurement": "temp", 514 | "policy": "default", 515 | "query": "SELECT mean(\"value\") FROM \"temp\" WHERE $timeFilter GROUP BY time($interval), \"location\" fill(previous)", 516 | "refId": "A", 517 | "resultFormat": "time_series", 518 | "select": [ 519 | [ 520 | { 521 | "params": [ 522 | "value" 523 | ], 524 | "type": "field" 525 | }, 526 | { 527 | "params": [], 528 | "type": "mean" 529 | } 530 | ] 531 | ], 532 | "tags": [] 533 | } 534 | ], 535 | "timeFrom": null, 536 | "timeShift": null, 537 | "title": "", 538 | "tooltip": { 539 | "msResolution": false, 540 | "shared": true, 541 | "value_type": "cumulative" 542 | }, 543 | "transparent": true, 544 | "type": "graph", 545 | "xaxis": { 546 | "show": true 547 | }, 548 | "yaxes": [ 549 | { 550 | "format": "celsius", 551 | "label": "", 552 | "logBase": 1, 553 | "max": null, 554 | "min": null, 555 | "show": true 556 | }, 557 | { 558 | "format": "short", 559 | "label": "", 560 | "logBase": 1, 561 | "max": null, 562 | "min": null, 563 | "show": true 564 | } 565 | ] 566 | } 567 | ], 568 | "title": "New row" 569 | } 570 | ], 571 | "time": { 572 | "from": "now-1h", 573 | "to": "now" 574 | }, 575 | "timepicker": { 576 | "now": true, 577 | "refresh_intervals": [ 578 | "5s", 579 | "10s", 580 | "30s", 581 | "1m", 582 | "5m", 583 | "15m", 584 | "30m", 585 | "1h", 586 | "2h", 587 | "1d" 588 | ], 589 | "time_options": [ 590 | "5m", 591 | "15m", 592 | "1h", 593 | "6h", 594 | "12h", 595 | "24h", 596 | "2d", 597 | "7d", 598 | "30d" 599 | ] 600 | }, 601 | "templating": { 602 | "list": [] 603 | }, 604 | "annotations": { 605 | "list": [] 606 | }, 607 | "refresh": "1m", 608 | "schemaVersion": 12, 609 | "version": 7, 610 | "links": [] 611 | } 612 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beastcraft-telemetry 2 | Santak `IPV-2012C` UPS, `DS18B20` one-wire temperature, ZTE `MF823` hostless modem + `A5-V11` (MiFi) router and `U-blox7` USB GPS monitoring with Grafana and Python on a Raspberry Pi 2 inside a motorhome. 3 | 4 | ### Installation 5 | I've built InfluxDB and Grafanates thanks to a number of existing [guides](https://github.com/ab77/beastcraft-telemetry/blob/master/grafana-armhf/README.md#references). 6 | 7 | # install InfluxDB 8 | wget https://s3.eu-central-1.amazonaws.com/belodetech/influxdb_0.13.0~209dd00_armhf.deb 9 | sudo dpkg -i influxdb_0.13.0~209dd00_armhf.deb 10 | sudo service influxdb start 11 | sudo update-rc.d influxdb defaults 95 10 12 | 13 | Install pre-built Node.js for Raspberry Pi using the handy [Adafruit](https://learn.adafruit.com/node-embedded-development/installing-node-dot-js) guide. 14 | 15 | # install Grafana 16 | wget https://s3.eu-central-1.amazonaws.com/belodetech/grafana_3.0.2-1463058303_armhf.deb 17 | sudo dpkg -i grafana_3.0.2-1463058303_armhf.deb 18 | sudo service grafana-server start 19 | sudo update-rc.d grafana-server defaults 95 10 20 | 21 | ### Configuration 22 | This repository contains configuration specific to my environment, with five `DS18B20` sensors in total. To personalise it: 23 | 24 | 1. run `pip install -r requirement.txt` 25 | 2. update `DS18B201` sensor list and database name in `w1_thermy.py` 26 | 3. [create database](https://docs.influxdata.com/influxdb/v0.12/introduction/getting_started/) (e.g. `grafana`) for your metrics by running `influx -execute 'CREATE DATABASE grafana;'` 27 | 4. optionally [create retention policy](https://docs.influxdata.com/influxdb/v0.12/guides/downsampling_and_retention/) otherwise `default` (store forever) RP applies 28 | 29 | #### Temperature Dashboard 30 | 31 | ![temperature dashboard](https://raw.githubusercontent.com/ab77/beastcraft-telemetry/master/static/temperature.png) 32 | 33 | 1. add `w1_therm.conf` to `/etc/supervisor/conf.d/` and reload `supervisor` process 34 | 2. go to [http://localhost:3000](http://localhost:3000), add a new datasource and configure other options 35 | 3. import `temp.json` dashboard and modify it to suit your needs or build your own from scratch (change URLs to suit your environment) 36 | 37 | #### UPS/Inverter Dashboard 38 | 39 | ![power dashboard](https://raw.githubusercontent.com/ab77/beastcraft-telemetry/master/static/power.png) 40 | 41 | 1. ensure [Network UPS Tools](http://www.networkupstools.org/) is correctly configured to work with the UPS (use `blazer_ser` driver) 42 | 2. edit `ups.py` and adjust the default `upsname` (mine is `upsoem`) or pass from the command line using `--ups` parameter 43 | 3. add `ups.conf` to `/etc/supervisor/conf.d/` and reload `supervisor` process 44 | 4. import `ups.json` dashboard and modify it to suit your needs or build your own from scratch (change URLs to suit your environment) 45 | 46 | It may be nesessary to modify UDEV rules and change the default `dialout` group assigned by kernel to the USB serial device to `nut` as follows: 47 | 48 | ``` 49 | printf "KERNEL==\"ttyUSB0\", GROUP=\"nut\"\n" > /etc/udev/rules.d/99_nut-serialups.rules 50 | ``` 51 | 52 | #### Traffic Dashboard 53 | 54 | 1. install `sflowtool` using [this](http://blog.sflow.com/2011/12/sflowtool.html) or [this](http://blog.belodedenko.me/2014/06/pretty-dashboards-with-fortios-sflow.html) guide 55 | 2. add `traffic.conf` to `/etc/supervisor/conf.d/` and reload `supervisor` process 56 | 3. import `traffic.json` dashboard and modify it to suit your needs or build your own from scratch 57 | 58 | #### Mobile Broadband Dashboard 59 | 60 | ![mobile dashboard](https://raw.githubusercontent.com/ab77/beastcraft-telemetry/master/static/mobilebroadband.png) 61 | 62 | 1. update `host` variable in `mobilebroadband.py` to match your ZTE MF823 modem IP address 63 | 2. add `mobilebroadband.conf` to `/etc/supervisor/conf.d/`, change DNS details and reload `supervisor` process 64 | 3. install `nginx` or `Caddyserver` and configure it as a reverse proxy for both Grafana and ZTE web GUIs (the later is required to set the `Referer` header) 65 | 4. import `mobilebroadband.json` dashboard and modify it to suit your needs or build your own from scratch (change URLs to suit your environment) 66 | 67 | ```` 68 | pi@localhost ~ $ cat /etc/nginx/sites-enabled/grafana 69 | server { 70 | 71 | listen 80; 72 | server_name ; 73 | 74 | location / { 75 | proxy_pass http://localhost:3000/; 76 | } 77 | 78 | location /public/ { 79 | alias /usr/share/grafana/public/; 80 | } 81 | 82 | location /goform/ { 83 | proxy_set_header Referer http:///; 84 | proxy_pass http://:80/goform/; 85 | } 86 | } 87 | ```` 88 | 89 | The ZTE MF823 has a REST API apart from the web GUI, which we are using to communicate with the modem from within the Grafana dashboard. The full list of commands isn't published, but looking at the modem's web interface with Chrome Developer Tools, the following commands were evident. 90 | 91 | ``` 92 | # connect mobile network (HTTP GET) 93 | http:///goform//goform_set_cmd_process?isTest=false¬Callback=true&goformId=CONNECT_NETWORK 94 | 95 | # disconnect mobile network (HTTP GET) 96 | http:///goform//goform_set_cmd_process?isTest=false¬Callback=true&goformId=DISCONNECT_NETWORK 97 | 98 | # modem state (HTTP GET) 99 | http:///goform/goform_get_cmd_process?multi_data=1&isTest=false&sms_received_flag_flag=0&sts_received_flag_flag=0&cmd=modem_main_state%2Cpin_status%2Cloginfo%2Cnew_version_state%2Ccurrent_upgrade_state%2Cis_mandatory%2Csms_received_flag%2Csts_received_flag%2Csignalbar%2Cnetwork_type%2Cnetwork_provider%2Cppp_status%2CEX_SSID1%2Cex_wifi_status%2CEX_wifi_profile%2Cm_ssid_enable%2Csms_unread_num%2CRadioOff%2Csimcard_roam%2Clan_ipaddr%2Cstation_mac%2Cbattery_charging%2Cbattery_vol_percent%2Cbattery_pers%2Cspn_display_flag%2Cplmn_display_flag%2Cspn_name_data%2Cspn_b1_flag%2Cspn_b2_flag%2Crealtime_tx_bytes%2Crealtime_rx_bytes%2Crealtime_time%2Crealtime_tx_thrpt%2Crealtime_rx_thrpt%2Cmonthly_rx_bytes%2Cmonthly_tx_bytes%2Cmonthly_time%2Cdate_month%2Cdata_volume_limit_switch%2Cdata_volume_limit_size%2Cdata_volume_alert_percent%2Cdata_volume_limit_unit%2Croam_setting_option%2Cupg_roam_switch%2Chplmn 100 | 101 | # ConnectionMode (dial mode auto|manual) 102 | http:///goform/goform_get_cmd_process?isTest=false&cmd=ConnectionMode 103 | 104 | # enable roaming 105 | http:///goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&roam_setting_option=on 106 | 107 | # disable roaming 108 | http:///goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&roam_setting_option=off 109 | 110 | # enable auto-dial 111 | http:///goform/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&ConnectionMode=auto_dial 112 | 113 | # disable auto-dial 114 | http:///goform/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&ConnectionMode=manual_dial 115 | 116 | # upgrade_result 117 | http:///goform/goform_get_cmd_process?isTest=false&cmd=upgrade_result 118 | 119 | # current_upgrade_state 120 | http:///goform/goform_get_cmd_process?isTest=false&cmd=current_upgrade_state 121 | 122 | # sms_data_total 123 | http:///goform/goform_get_cmd_process?isTest=false&cmd=sms_data_total&page=0&data_per_page=500&mem_store=1&tags=10&order_by=order+by+id+desc 124 | 125 | # sms_capacity_info 126 | http:///goform/goform_get_cmd_process?isTest=false&cmd=sms_capacity_info 127 | 128 | # sms_parameter_info 129 | http:///goform/goform_get_cmd_process?isTest=false&cmd=sms_parameter_info 130 | 131 | # pbm_init_flag 132 | http:///goform/goform_get_cmd_process?isTest=false&cmd=pbm_init_flag 133 | 134 | # pbm_capacity_info&pbm_location=pbm_sim 135 | http:///goform/goform_get_cmd_process?isTest=false&cmd=pbm_capacity_info&pbm_location=pbm_sim 136 | 137 | # mem__data_total 138 | http:///goform/goform_get_cmd_process?isTest=false&mem_store=2&cmd=pbm_data_total&page=0&data_per_page=2000&orderBy=name&isAsc=true 139 | 140 | # m_ssid_enable 141 | http:///goform/goform_get_cmd_process?isTest=false&cmd=m_ssid_enable%2CSSID1%2CAuthMode%2CHideSSID%2CWPAPSK1%2CMAX_Access_num%2CEncrypType%2Cm_SSID%2Cm_AuthMode%2Cm_HideSSID%2Cm_WPAPSK1%2Cm_MAX_Access_num%2Cm_EncrypType&multi_data=1 142 | 143 | # pbm_capacity_info&pbm_location=pbm_native 144 | http:///goform/goform_get_cmd_process?isTest=false&cmd=pbm_capacity_info&pbm_location=pbm_native 145 | 146 | # sim_imsi 147 | http:///goform/goform_get_cmd_process?isTest=false&multi_data=1&cmd=sim_imsi%2Csim_imsi_lists 148 | 149 | # wifi_coverage 150 | http:///goform/goform_get_cmd_process?isTest=false&cmd=wifi_coverage%2Cm_ssid_enable%2Cimei%2Cweb_version%2Cwa_inner_version%2Chardware_version%2CMAX_Access_num%2CSSID1%2Cm_SSID%2Cm_HideSSID%2Cm_MAX_Access_num%2Clan_ipaddr%2Cwan_active_band%2Cmac_address%2Cmsisdn%2CLocalDomain%2Cwan_ipaddr%2Cipv6_wan_ipaddr%2Cipv6_pdp_type%2Cpdp_type%2Cppp_status%2Csim_iccid%2Csim_imsi%2Crmcc%2Crmnc%2Crssi%2Crscp%2Clte_rsrp%2Cecio%2Clte_snr%2Cnetwork_type%2Clte_rssi%2Clac_code%2Ccell_id%2Clte_pci%2Cdns_mode%2Cprefer_dns_manual%2Cstandby_dns_manual%2Cprefer_dns_auto%2Cstandby_dns_auto%2Cipv6_dns_mode%2Cipv6_prefer_dns_manual%2Cipv6_standby_dns_manual%2Cipv6_prefer_dns_auto%2Cipv6_standby_dns_auto%2Cmodel_name&multi_data=1 151 | 152 | # current_network_mode 153 | http:///goform/goform_get_cmd_process?isTest=false&cmd=current_network_mode%2Cm_netselect_save%2Cnet_select_mode%2Cm_netselect_contents%2Cnet_select%2Cppp_status%2Cmodem_main_state%2Clte_band_lock%2Cwcdma_band_lock&multi_data=1 154 | 155 | # APN_config0 156 | http:///goform/goform_get_cmd_process?isTest=false&cmd=APN_config0%2CAPN_config1%2CAPN_config2%2CAPN_config3%2CAPN_config4%2CAPN_config5%2CAPN_config6%2CAPN_config7%2CAPN_config8%2CAPN_config9%2CAPN_config10%2CAPN_config11%2CAPN_config12%2CAPN_config13%2CAPN_config14%2CAPN_config15%2CAPN_config16%2CAPN_config17%2CAPN_config18%2CAPN_config19%2Cipv6_APN_config0%2Cipv6_APN_config1%2Cipv6_APN_config2%2Cipv6_APN_config3%2Cipv6_APN_config4%2Cipv6_APN_config5%2Cipv6_APN_config6%2Cipv6_APN_config7%2Cipv6_APN_config8%2Cipv6_APN_config9%2Cipv6_APN_config10%2Cipv6_APN_config11%2Cipv6_APN_config12%2Cipv6_APN_config13%2Cipv6_APN_config14%2Cipv6_APN_config15%2Cipv6_APN_config16%2Cipv6_APN_config17%2Cipv6_APN_config18%2Cipv6_APN_config19%2Cm_profile_name%2Cprofile_name%2Cwan_dial%2Capn_select%2Cpdp_type%2Cpdp_select%2Cpdp_addr%2Cindex%2CCurrent_index%2Capn_auto_config%2Cipv6_apn_auto_config%2Capn_mode%2Cwan_apn%2Cppp_auth_mode%2Cppp_username%2Cppp_passwd%2Cdns_mode%2Cprefer_dns_manual%2Cstandby_dns_manual%2Cipv6_wan_apn%2Cipv6_pdp_type%2Cipv6_ppp_auth_mode%2Cipv6_ppp_username%2Cipv6_ppp_passwd%2Cipv6_dns_mode%2Cipv6_prefer_dns_manual%2Cipv6_standby_dns_manual&multi_data=1 157 | 158 | # lan_ipaddr 159 | http:///goform/goform_get_cmd_process?isTest=false&cmd=lan_ipaddr%2Clan_netmask%2Cmac_address%2CdhcpEnabled%2CdhcpStart%2CdhcpEnd%2CdhcpLease_hour&multi_data=1 160 | 161 | # DMZEnable 162 | http:///goform/goform_get_cmd_process?isTest=false&cmd=DMZEnable%2CDMZIPAddress&multi_data=1 163 | 164 | # PortMapEnable 165 | http:///goform/goform_get_cmd_process?isTest=false&cmd=PortMapEnable%2CPortMapRules_0%2CPortMapRules_1%2CPortMapRules_2%2CPortMapRules_3%2CPortMapRules_4%2CPortMapRules_5%2CPortMapRules_6%2CPortMapRules_7%2CPortMapRules_8%2CPortMapRules_9&multi_data=1 166 | 167 | # IPPortFilterEnable 168 | http:///goform/goform_get_cmd_process?isTest=false&cmd=IPPortFilterEnable%2CDefaultFirewallPolicy%2CIPPortFilterRules_0%2CIPPortFilterRules_1%2CIPPortFilterRules_2%2CIPPortFilterRules_3%2CIPPortFilterRules_4%2CIPPortFilterRules_5%2CIPPortFilterRules_6%2CIPPortFilterRules_7%2CIPPortFilterRules_8%2CIPPortFilterRules_9%2CIPPortFilterRulesv6_0%2CIPPortFilterRulesv6_1%2CIPPortFilterRulesv6_2%2CIPPortFilterRulesv6_3%2CIPPortFilterRulesv6_4%2CIPPortFilterRulesv6_5%2CIPPortFilterRulesv6_6%2CIPPortFilterRulesv6_7%2CIPPortFilterRulesv6_8%2CIPPortFilterRulesv6_9&multi_data=1 169 | ``` 170 | 171 | All the API requests require the `Referer: http:///` request header present. No additional headers are required. 172 | 173 | #### Satellite Internet Dashboard 174 | 175 | ![mobile dashboard](https://raw.githubusercontent.com/ab77/beastcraft-telemetry/master/static/sat.png) 176 | 177 | 1. update `host` variable in `satinternet.py` to match the Newtec S3P (NTC2252) modem ipaddr 178 | 2. add `satinternet.conf` to `/etc/supervisor/conf.d/` and reload `supervisor` process 179 | 3. install `nginx` or `Caddyserver` and configure it as a reverse proxy for Newtec Sat3Play web GUI 180 | 4. import `satinternet.json` dashboard and modify it to suit your needs or build your own from scratch (change URLs to suit your environment) 181 | 182 | #### GPS Dashboard 183 | 184 | ![GPS dashboard](https://raw.githubusercontent.com/ab77/beastcraft-telemetry/master/static/gps.png) 185 | 186 | 1. install `gpsd` ([docs](http://www.catb.org/gpsd/installation.html)) using [this](http://www.danmandle.com/blog/getting-gpsd-to-work-with-python/) or [this](http://blog.perrygeo.net/2007/05/27/python-gpsd-bindings/) guide or better still, install latest from [source](http://git.savannah.gnu.org/cgit/gpsd.git). 187 | 2. add `geo.conf` to `/etc/supervisor/conf.d/` and reload `supervisor` process 188 | 3. import `gps.json` dashboard and modify it to suit your needs or build your own from scratch 189 | 190 | To synchronise time using GPS receiver and NTP using SHared Memory driver [(type 28)](http://doc.ntp.org/4.2.6/drivers/driver28.html), read the following concise article [Connecting u-blox NEO-6M GPS to Raspberry Pi](https://bigdanzblog.wordpress.com/2015/01/18/connecting-u-blox-neo-6m-gps-to-raspberry-pi/). I had a lot of problems with the GPSd `v3.06` in the Raspbian Wheezy repository, so I upgraded to Jessie, built GPSd `v3.16` from source and installed `systemd` services. The `/etc/default/gpsd` file looks like this: 191 | 192 | ``` 193 | # Default settings for gpsd. 194 | START_DAEMON="true" 195 | GPSD_OPTIONS="-n" 196 | DEVICES="/dev/ttyACM0" 197 | USBAUTO="true" 198 | GPSD_SOCKET="/var/run/gpsd.sock" 199 | ``` 200 | 201 | Usign type 28 (SHM), `ntp.conf` looks like this: 202 | 203 | ``` 204 | # using SHaredMemory (SHM) driver 205 | server 127.127.28.0 minpoll 4 maxpoll 4 iburst prefer 206 | fudge 127.127.28.0 time1 +0.080 flag1 1 refid GPSD stratum 1 207 | ``` 208 | 209 | I've also tried [using driver type 20 for NTP](http://www.satsignal.eu/ntp/RaspberryPi-notes.html), however I couldn't get NTP and GPSD to play together nicely in the mode, so I reverted to SHM. With [type 20](http://doc.ntp.org/4.2.6/drivers/driver20.html) `ntp.conf` looks like this: 210 | 211 | ``` 212 | # GPS receiver time source via /dev/gsp0, no SHaredMemory (SHM) driver 213 | server 127.127.20.0 mode 16 minpoll 4 maxpoll 4 prefer # set /dev/gps0 9600 baud 214 | fudge 127.127.20.0 flag1 0 # disable PPS as it isn't present with cheap USB GPS(s) 215 | ``` 216 | 217 | The `/etc/udev/rules.d/99-gpsd.rules` makes sure the device has the right permissions and the symbolic link persists on restart: 218 | 219 | ``` 220 | KERNEL=="ttyACM[0-9]*", GROUP="dialout", MODE="0666" 221 | KERNEL=="ttyACM0", SYMLINK+="gps0" 222 | ``` 223 | 224 | To reload `udev` rules without rebooting, run `sudo udevadm control --reload-rules`. 225 | 226 | Checking time sync. results: 227 | ``` 228 | # ntpq -p 229 | remote refid st t when poll reach delay offset jitter 230 | ============================================================================== 231 | *SHM(0) .GPSD. 1 l 3 16 377 0.000 -1.577 1.830 232 | ``` 233 | 234 | My time seems to be off by about 80ms, which I correct with `time1 +0.080` option. The resulting accuracy means the clock is off by less than +/- 10ms, which is good enough for my purposes of keeping the time roughly in sync with the world. 235 | 236 | ``` 237 | # ntpdate -d 0.europe.pool.ntp.org 238 | ... 239 | delay 0.07957, dispersion 0.00102 240 | offset -0.004779 241 | 242 | 19 May 10:23:48 ntpdate[2993]: adjust time server 85.25.197.197 offset -0.004779 sec 243 | ``` 244 | 245 | To set initial datetime from GSP, add the following to `/etc/rc.local` to run once per boot: 246 | 247 | ``` 248 | # set initial datetime from GPS 249 | influx -database beastcraft \ 250 | --format csv \ 251 | -execute 'SELECT last("value") FROM "time";' | \ 252 | tail -1 | awk -F',' '{print $3}' | \ 253 | xargs date +%Y-%m-%dT%H:%M:%S.000Z -s 254 | ``` 255 | 256 | #### FortiWifi Interface Monitor 257 | 258 | A `FortiWifi` firewall can be configured as a wireless bridge as follows[n1]: 259 | 260 | ``` 261 | config system global 262 | set wireless-mode client 263 | end 264 | ``` 265 | 266 | A side effect of doing this, is that the resulting `wifi` internal interface is always up, regardless of whether or not it is connected to the upstream wireless network. This means that if a backup interface is to be used (e.g. Mobile Broadband), the `wifi` interface default route will never be released and the backup one will never kick in. A less elegant solution is to use FortiOS `link-monitor` function in conjunction with a custom script running somewhere nearby to manipulate network routes depending on interface availability. 267 | 268 | For example, if a `wifi` interface is used as a primary network link and a `wan2` interface is used for backup, the following `link-monitor` configuration is set on the device: 269 | 270 | ``` 271 | config system link-monitor 272 | edit "wan2" 273 | set srcintf "wan2" 274 | set server "8.8.8.8" "8.8.4.4" 275 | next 276 | edit "wifi" 277 | set srcintf "wifi" 278 | set server "8.8.8.8" "8.8.4.4" 279 | next 280 | end 281 | ```` 282 | 283 | Status check is performed for running `diag sys link-monitor status wifi` command and inspecting the output: 284 | 285 | ``` 286 | Link Monitor: wifi Status: alive Create time: Fri Mar 18 14:55:41 2016 287 | Source interface: wifi (18) 288 | Interval: 5, Timeout 1 289 | Fail times: 0/5 290 | Send times: 0 291 | Peer: 8.8.4.4(8.8.4.4) 292 | Source IP(172.16.99.11) 293 | Route: 172.16.99.11->8.8.4.4/32, gwy(172.16.99.254) 294 | protocol: ping, state: alive 295 | Latency(recent/average): 20.55/23.82 ms Jitter: 267.41 296 | Recovery times(0/5) 297 | Continuous sending times after the first recovery time 0 298 | Packet sent: 172173 Packet received: 167884 299 | Peer: 8.8.8.8(8.8.8.8) 300 | Source IP(172.16.99.11) 301 | Route: 172.16.99.11->8.8.8.8/32, gwy(172.16.99.254) 302 | protocol: ping, state: alive 303 | Latency(recent/average): 20.41/29.20 ms Jitter: 266.55 304 | Recovery times(1/5) 305 | Continuous sending times after the first recovery time 1 306 | Packet sent: 172161 Packet received: 169386 307 | ``` 308 | 309 | To automate this, I've written a simple Python script, which can be run on a nearby Linux host, to poll the firewall every few seconds and check the interface status. Should the primary interface go down, the script modifies the default route to send traffic to the backup interface: 310 | 311 | ``` 312 | usage: monitor.py [-h] --host HOST [--port PORT] [--user USER] --iface IFACE 313 | --backup BACKUP --gwip GWIP 314 | 315 | FortiGate interface monitor 316 | 317 | optional arguments: 318 | -h, --help show this help message and exit 319 | --host HOST FortiGate appliance hostname or IP 320 | --port PORT SSH port of the FortiGate appliance 321 | --user USER FortiGate admin username 322 | --iface IFACE FortiGate interface to monitor (e.g. wifi) 323 | --backup BACKUP FortiGate interface to fail-over to (e.g. wan2) 324 | --gwip GWIP Backup interface gateway ipaddr 325 | ``` 326 | 327 | [![ab1](https://avatars2.githubusercontent.com/u/2033996?v=3&s=96)](http://ab77.github.io/) 328 | 329 | ###### Footnotes 330 | 331 | [n1] [FortiWifi Client mode (wireless bridge)](https://stuff.purdon.ca/?page_id=183) 332 | -------------------------------------------------------------------------------- /traffic.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 4, 3 | "title": "Traffic", 4 | "originalTitle": "Traffic", 5 | "tags": [ 6 | "internet", 7 | "traffic" 8 | ], 9 | "style": "dark", 10 | "timezone": "browser", 11 | "editable": true, 12 | "hideControls": false, 13 | "sharedCrosshair": false, 14 | "rows": [ 15 | { 16 | "collapse": false, 17 | "editable": true, 18 | "height": "250px", 19 | "panels": [ 20 | { 21 | "aliasColors": {}, 22 | "bars": false, 23 | "datasource": "BeastCraft", 24 | "editable": true, 25 | "error": false, 26 | "fill": 1, 27 | "grid": { 28 | "threshold1": null, 29 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 30 | "threshold2": null, 31 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 32 | }, 33 | "id": 3, 34 | "interval": "1m", 35 | "isNew": true, 36 | "legend": { 37 | "avg": false, 38 | "current": false, 39 | "max": false, 40 | "min": false, 41 | "show": true, 42 | "total": false, 43 | "values": false 44 | }, 45 | "lines": true, 46 | "linewidth": 2, 47 | "links": [], 48 | "nullPointMode": "connected", 49 | "percentage": false, 50 | "pointradius": 5, 51 | "points": false, 52 | "renderer": "flot", 53 | "seriesOverrides": [], 54 | "span": 12, 55 | "stack": false, 56 | "steppedLine": false, 57 | "targets": [ 58 | { 59 | "alias": "inBytes", 60 | "dsType": "influxdb", 61 | "groupBy": [ 62 | { 63 | "params": [ 64 | "$interval" 65 | ], 66 | "type": "time" 67 | }, 68 | { 69 | "params": [ 70 | "ifIndex" 71 | ], 72 | "type": "tag" 73 | }, 74 | { 75 | "params": [ 76 | "null" 77 | ], 78 | "type": "fill" 79 | } 80 | ], 81 | "measurement": "ifInOctets", 82 | "query": "SELECT derivative(mean(\"value\"), 1m) * 8 FROM \"ifInOctets\" WHERE \"ifIndex\" = '9' AND $timeFilter GROUP BY time($interval), \"ifIndex\" fill(null)", 83 | "rawQuery": false, 84 | "refId": "A", 85 | "resultFormat": "time_series", 86 | "select": [ 87 | [ 88 | { 89 | "params": [ 90 | "value" 91 | ], 92 | "type": "field" 93 | }, 94 | { 95 | "params": [], 96 | "type": "mean" 97 | }, 98 | { 99 | "params": [ 100 | "1m" 101 | ], 102 | "type": "derivative" 103 | }, 104 | { 105 | "params": [ 106 | "* 8" 107 | ], 108 | "type": "math" 109 | } 110 | ] 111 | ], 112 | "tags": [ 113 | { 114 | "key": "ifIndex", 115 | "operator": "=", 116 | "value": "9" 117 | } 118 | ], 119 | "policy": "default" 120 | }, 121 | { 122 | "alias": "outBytes", 123 | "dsType": "influxdb", 124 | "groupBy": [ 125 | { 126 | "params": [ 127 | "$interval" 128 | ], 129 | "type": "time" 130 | }, 131 | { 132 | "params": [ 133 | "ifIndex" 134 | ], 135 | "type": "tag" 136 | }, 137 | { 138 | "params": [ 139 | "null" 140 | ], 141 | "type": "fill" 142 | } 143 | ], 144 | "measurement": "ifOutOctets", 145 | "query": "SELECT derivative(mean(\"value\"), 1m) * 8 FROM \"ifOutOctets\" WHERE \"ifIndex\" = '9' AND $timeFilter GROUP BY time($interval), \"ifIndex\" fill(null)", 146 | "refId": "B", 147 | "resultFormat": "time_series", 148 | "select": [ 149 | [ 150 | { 151 | "params": [ 152 | "value" 153 | ], 154 | "type": "field" 155 | }, 156 | { 157 | "params": [], 158 | "type": "mean" 159 | }, 160 | { 161 | "params": [ 162 | "1m" 163 | ], 164 | "type": "derivative" 165 | }, 166 | { 167 | "params": [ 168 | "* 8" 169 | ], 170 | "type": "math" 171 | } 172 | ] 173 | ], 174 | "tags": [ 175 | { 176 | "key": "ifIndex", 177 | "operator": "=", 178 | "value": "9" 179 | } 180 | ], 181 | "policy": "default" 182 | } 183 | ], 184 | "timeFrom": null, 185 | "timeShift": null, 186 | "title": "", 187 | "tooltip": { 188 | "shared": true, 189 | "value_type": "cumulative", 190 | "msResolution": false 191 | }, 192 | "transparent": true, 193 | "type": "graph", 194 | "yaxes": [ 195 | { 196 | "show": true, 197 | "min": 0, 198 | "max": null, 199 | "logBase": 1, 200 | "format": "bits", 201 | "label": "" 202 | }, 203 | { 204 | "show": true, 205 | "min": null, 206 | "max": null, 207 | "logBase": 1, 208 | "format": "short" 209 | } 210 | ], 211 | "xaxis": { 212 | "show": true 213 | } 214 | } 215 | ], 216 | "title": "LAN" 217 | }, 218 | { 219 | "collapse": false, 220 | "editable": true, 221 | "height": "250px", 222 | "panels": [ 223 | { 224 | "aliasColors": {}, 225 | "bars": false, 226 | "datasource": "BeastCraft", 227 | "editable": true, 228 | "error": false, 229 | "fill": 1, 230 | "grid": { 231 | "threshold1": null, 232 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 233 | "threshold2": null, 234 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 235 | }, 236 | "id": 2, 237 | "interval": "1m", 238 | "isNew": true, 239 | "legend": { 240 | "avg": false, 241 | "current": false, 242 | "max": false, 243 | "min": false, 244 | "show": true, 245 | "total": false, 246 | "values": false 247 | }, 248 | "lines": true, 249 | "linewidth": 2, 250 | "links": [], 251 | "nullPointMode": "connected", 252 | "percentage": false, 253 | "pointradius": 5, 254 | "points": false, 255 | "renderer": "flot", 256 | "seriesOverrides": [], 257 | "span": 12, 258 | "stack": false, 259 | "steppedLine": false, 260 | "targets": [ 261 | { 262 | "alias": "inBytes", 263 | "dsType": "influxdb", 264 | "groupBy": [ 265 | { 266 | "params": [ 267 | "$interval" 268 | ], 269 | "type": "time" 270 | }, 271 | { 272 | "params": [ 273 | "ifIndex" 274 | ], 275 | "type": "tag" 276 | }, 277 | { 278 | "params": [ 279 | "null" 280 | ], 281 | "type": "fill" 282 | } 283 | ], 284 | "measurement": "ifInOctets", 285 | "query": "SELECT derivative(mean(\"value\"), 1m) * 8 FROM \"ifInOctets\" WHERE \"ifIndex\" = '2' AND $timeFilter GROUP BY time($interval), \"ifIndex\" fill(null)", 286 | "rawQuery": false, 287 | "refId": "A", 288 | "resultFormat": "time_series", 289 | "select": [ 290 | [ 291 | { 292 | "params": [ 293 | "value" 294 | ], 295 | "type": "field" 296 | }, 297 | { 298 | "params": [], 299 | "type": "mean" 300 | }, 301 | { 302 | "params": [ 303 | "1m" 304 | ], 305 | "type": "derivative" 306 | }, 307 | { 308 | "params": [ 309 | "* 8" 310 | ], 311 | "type": "math" 312 | } 313 | ] 314 | ], 315 | "tags": [ 316 | { 317 | "key": "ifIndex", 318 | "operator": "=", 319 | "value": "2" 320 | } 321 | ], 322 | "policy": "default" 323 | }, 324 | { 325 | "alias": "outBytes", 326 | "dsType": "influxdb", 327 | "groupBy": [ 328 | { 329 | "params": [ 330 | "$interval" 331 | ], 332 | "type": "time" 333 | }, 334 | { 335 | "params": [ 336 | "ifIndex" 337 | ], 338 | "type": "tag" 339 | }, 340 | { 341 | "params": [ 342 | "null" 343 | ], 344 | "type": "fill" 345 | } 346 | ], 347 | "measurement": "ifOutOctets", 348 | "query": "SELECT derivative(mean(\"value\"), 1m) * 8 FROM \"ifOutOctets\" WHERE \"ifIndex\" = '2' AND $timeFilter GROUP BY time($interval), \"ifIndex\" fill(null)", 349 | "refId": "B", 350 | "resultFormat": "time_series", 351 | "select": [ 352 | [ 353 | { 354 | "params": [ 355 | "value" 356 | ], 357 | "type": "field" 358 | }, 359 | { 360 | "params": [], 361 | "type": "mean" 362 | }, 363 | { 364 | "params": [ 365 | "1m" 366 | ], 367 | "type": "derivative" 368 | }, 369 | { 370 | "params": [ 371 | "* 8" 372 | ], 373 | "type": "math" 374 | } 375 | ] 376 | ], 377 | "tags": [ 378 | { 379 | "key": "ifIndex", 380 | "operator": "=", 381 | "value": "2" 382 | } 383 | ], 384 | "policy": "default" 385 | } 386 | ], 387 | "timeFrom": null, 388 | "timeShift": null, 389 | "title": "", 390 | "tooltip": { 391 | "shared": true, 392 | "value_type": "cumulative", 393 | "msResolution": false 394 | }, 395 | "transparent": true, 396 | "type": "graph", 397 | "yaxes": [ 398 | { 399 | "show": true, 400 | "min": 0, 401 | "max": null, 402 | "logBase": 1, 403 | "format": "bits", 404 | "label": "" 405 | }, 406 | { 407 | "show": true, 408 | "min": null, 409 | "max": null, 410 | "logBase": 1, 411 | "format": "short" 412 | } 413 | ], 414 | "xaxis": { 415 | "show": true 416 | } 417 | } 418 | ], 419 | "title": "Mobile" 420 | }, 421 | { 422 | "collapse": true, 423 | "editable": true, 424 | "height": "250px", 425 | "panels": [ 426 | { 427 | "aliasColors": {}, 428 | "bars": false, 429 | "datasource": null, 430 | "editable": true, 431 | "error": false, 432 | "fill": 1, 433 | "grid": { 434 | "threshold1": null, 435 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 436 | "threshold2": null, 437 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 438 | }, 439 | "id": 1, 440 | "interval": "1m", 441 | "isNew": true, 442 | "legend": { 443 | "avg": false, 444 | "current": false, 445 | "max": false, 446 | "min": false, 447 | "show": true, 448 | "total": false, 449 | "values": false 450 | }, 451 | "lines": true, 452 | "linewidth": 2, 453 | "links": [], 454 | "nullPointMode": "connected", 455 | "percentage": false, 456 | "pointradius": 5, 457 | "points": false, 458 | "renderer": "flot", 459 | "seriesOverrides": [], 460 | "span": 12, 461 | "stack": false, 462 | "steppedLine": false, 463 | "targets": [ 464 | { 465 | "alias": "inBytes", 466 | "dsType": "influxdb", 467 | "groupBy": [ 468 | { 469 | "params": [ 470 | "$interval" 471 | ], 472 | "type": "time" 473 | }, 474 | { 475 | "params": [ 476 | "ifIndex" 477 | ], 478 | "type": "tag" 479 | }, 480 | { 481 | "params": [ 482 | "null" 483 | ], 484 | "type": "fill" 485 | } 486 | ], 487 | "measurement": "ifInOctets", 488 | "query": "SELECT derivative(mean(\"value\"), 1m) * 8 FROM \"ifInOctets\" WHERE \"ifIndex\" = '1' AND $timeFilter GROUP BY time($interval), \"ifIndex\" fill(null)", 489 | "rawQuery": false, 490 | "refId": "A", 491 | "resultFormat": "time_series", 492 | "select": [ 493 | [ 494 | { 495 | "params": [ 496 | "value" 497 | ], 498 | "type": "field" 499 | }, 500 | { 501 | "params": [], 502 | "type": "mean" 503 | }, 504 | { 505 | "params": [ 506 | "1m" 507 | ], 508 | "type": "derivative" 509 | }, 510 | { 511 | "params": [ 512 | "* 8" 513 | ], 514 | "type": "math" 515 | } 516 | ] 517 | ], 518 | "tags": [ 519 | { 520 | "key": "ifIndex", 521 | "operator": "=", 522 | "value": "1" 523 | } 524 | ] 525 | }, 526 | { 527 | "alias": "outBytes", 528 | "dsType": "influxdb", 529 | "groupBy": [ 530 | { 531 | "params": [ 532 | "$interval" 533 | ], 534 | "type": "time" 535 | }, 536 | { 537 | "params": [ 538 | "ifIndex" 539 | ], 540 | "type": "tag" 541 | }, 542 | { 543 | "params": [ 544 | "null" 545 | ], 546 | "type": "fill" 547 | } 548 | ], 549 | "measurement": "ifOutOctets", 550 | "query": "SELECT derivative(mean(\"value\"), 1m) * 8 FROM \"ifOutOctets\" WHERE \"ifIndex\" = '1' AND $timeFilter GROUP BY time($interval), \"ifIndex\" fill(null)", 551 | "refId": "B", 552 | "resultFormat": "time_series", 553 | "select": [ 554 | [ 555 | { 556 | "params": [ 557 | "value" 558 | ], 559 | "type": "field" 560 | }, 561 | { 562 | "params": [], 563 | "type": "mean" 564 | }, 565 | { 566 | "params": [ 567 | "1m" 568 | ], 569 | "type": "derivative" 570 | }, 571 | { 572 | "params": [ 573 | "* 8" 574 | ], 575 | "type": "math" 576 | } 577 | ] 578 | ], 579 | "tags": [ 580 | { 581 | "key": "ifIndex", 582 | "operator": "=", 583 | "value": "1" 584 | } 585 | ] 586 | } 587 | ], 588 | "timeFrom": null, 589 | "timeShift": null, 590 | "title": "", 591 | "tooltip": { 592 | "shared": true, 593 | "value_type": "cumulative" 594 | }, 595 | "transparent": true, 596 | "type": "graph", 597 | "yaxes": [ 598 | { 599 | "show": true, 600 | "min": 0, 601 | "max": null, 602 | "logBase": 1, 603 | "format": "bits", 604 | "label": "" 605 | }, 606 | { 607 | "show": true, 608 | "min": null, 609 | "max": null, 610 | "logBase": 1, 611 | "format": "short" 612 | } 613 | ], 614 | "xaxis": { 615 | "show": true 616 | } 617 | } 618 | ], 619 | "title": "Satellite" 620 | } 621 | ], 622 | "time": { 623 | "from": "now-1h", 624 | "to": "now" 625 | }, 626 | "timepicker": { 627 | "now": true, 628 | "refresh_intervals": [ 629 | "5s", 630 | "10s", 631 | "30s", 632 | "1m", 633 | "5m", 634 | "15m", 635 | "30m", 636 | "1h", 637 | "2h", 638 | "1d" 639 | ], 640 | "time_options": [ 641 | "5m", 642 | "15m", 643 | "1h", 644 | "6h", 645 | "12h", 646 | "24h", 647 | "2d", 648 | "7d", 649 | "30d" 650 | ] 651 | }, 652 | "templating": { 653 | "list": [] 654 | }, 655 | "annotations": { 656 | "list": [] 657 | }, 658 | "refresh": "1m", 659 | "schemaVersion": 12, 660 | "version": 1, 661 | "links": [] 662 | } 663 | -------------------------------------------------------------------------------- /satinternet.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 6, 3 | "title": "Satellite Internet", 4 | "originalTitle": "Satellite Internet", 5 | "tags": [ 6 | "broadband", 7 | "satellite" 8 | ], 9 | "style": "dark", 10 | "timezone": "browser", 11 | "editable": true, 12 | "hideControls": false, 13 | "sharedCrosshair": false, 14 | "rows": [ 15 | { 16 | "collapse": false, 17 | "editable": true, 18 | "height": "0", 19 | "panels": [ 20 | { 21 | "cacheTimeout": null, 22 | "colorBackground": false, 23 | "colorValue": false, 24 | "colors": [ 25 | "rgba(245, 54, 54, 0.9)", 26 | "rgba(237, 129, 40, 0.89)", 27 | "rgba(50, 172, 45, 0.97)" 28 | ], 29 | "datasource": "BeastCraft", 30 | "editable": true, 31 | "error": false, 32 | "format": "none", 33 | "gauge": { 34 | "maxValue": 100, 35 | "minValue": 0, 36 | "show": false, 37 | "thresholdLabels": false, 38 | "thresholdMarkers": true 39 | }, 40 | "height": "100px", 41 | "id": 1, 42 | "interval": null, 43 | "isNew": true, 44 | "links": [ 45 | { 46 | "targetBlank": true, 47 | "title": "Modem", 48 | "type": "absolute", 49 | "url": "http://s3p-web.beastcraft.belodedenko.me/" 50 | } 51 | ], 52 | "maxDataPoints": 100, 53 | "nullPointMode": "connected", 54 | "nullText": null, 55 | "postfix": "", 56 | "postfixFontSize": "50%", 57 | "prefix": "", 58 | "prefixFontSize": "50%", 59 | "span": 4, 60 | "sparkline": { 61 | "fillColor": "rgba(31, 118, 189, 0.18)", 62 | "full": false, 63 | "lineColor": "rgb(31, 120, 193)", 64 | "show": false 65 | }, 66 | "targets": [ 67 | { 68 | "dsType": "influxdb", 69 | "groupBy": [], 70 | "measurement": "Modem_State", 71 | "policy": "default", 72 | "query": "SELECT last(\"value\") FROM \"Modem_State\" WHERE $timeFilter", 73 | "rawQuery": true, 74 | "refId": "A", 75 | "resultFormat": "time_series", 76 | "select": [ 77 | [ 78 | { 79 | "params": [ 80 | "value" 81 | ], 82 | "type": "field" 83 | } 84 | ] 85 | ], 86 | "tags": [] 87 | } 88 | ], 89 | "thresholds": "", 90 | "title": "Status", 91 | "transparent": true, 92 | "type": "singlestat", 93 | "valueFontSize": "80%", 94 | "valueMaps": [ 95 | { 96 | "value": "null", 97 | "op": "=", 98 | "text": "N/A" 99 | } 100 | ], 101 | "valueName": "current" 102 | }, 103 | { 104 | "cacheTimeout": null, 105 | "colorBackground": false, 106 | "colorValue": false, 107 | "colors": [ 108 | "rgba(245, 54, 54, 0.9)", 109 | "rgba(237, 129, 40, 0.89)", 110 | "rgba(50, 172, 45, 0.97)" 111 | ], 112 | "datasource": "BeastCraft", 113 | "editable": true, 114 | "error": false, 115 | "format": "none", 116 | "gauge": { 117 | "maxValue": 100, 118 | "minValue": 0, 119 | "show": false, 120 | "thresholdLabels": false, 121 | "thresholdMarkers": true 122 | }, 123 | "height": "100px", 124 | "id": 8, 125 | "interval": null, 126 | "isNew": true, 127 | "links": [], 128 | "maxDataPoints": 100, 129 | "nullPointMode": "connected", 130 | "nullText": null, 131 | "postfix": "", 132 | "postfixFontSize": "50%", 133 | "prefix": "", 134 | "prefixFontSize": "50%", 135 | "span": 2, 136 | "sparkline": { 137 | "fillColor": "rgba(31, 118, 189, 0.18)", 138 | "full": false, 139 | "lineColor": "rgb(31, 120, 193)", 140 | "show": true 141 | }, 142 | "targets": [ 143 | { 144 | "dsType": "influxdb", 145 | "groupBy": [], 146 | "measurement": "Software_state", 147 | "policy": "default", 148 | "query": "SELECT last(\"value\") FROM \"Software_state\" WHERE $timeFilter", 149 | "rawQuery": true, 150 | "refId": "A", 151 | "resultFormat": "time_series", 152 | "select": [ 153 | [ 154 | { 155 | "params": [ 156 | "value" 157 | ], 158 | "type": "field" 159 | } 160 | ] 161 | ], 162 | "tags": [] 163 | } 164 | ], 165 | "thresholds": "", 166 | "title": "Software", 167 | "transparent": true, 168 | "type": "singlestat", 169 | "valueFontSize": "50%", 170 | "valueMaps": [ 171 | { 172 | "value": "null", 173 | "op": "=", 174 | "text": "N/A" 175 | } 176 | ], 177 | "valueName": "current" 178 | }, 179 | { 180 | "cacheTimeout": null, 181 | "colorBackground": false, 182 | "colorValue": false, 183 | "colors": [ 184 | "rgba(245, 54, 54, 0.9)", 185 | "rgba(237, 129, 40, 0.89)", 186 | "rgba(50, 172, 45, 0.97)" 187 | ], 188 | "datasource": "BeastCraft", 189 | "editable": true, 190 | "error": false, 191 | "format": "none", 192 | "gauge": { 193 | "maxValue": 100, 194 | "minValue": 0, 195 | "show": false, 196 | "thresholdLabels": false, 197 | "thresholdMarkers": true 198 | }, 199 | "height": "100px", 200 | "id": 2, 201 | "interval": null, 202 | "isNew": true, 203 | "links": [], 204 | "maxDataPoints": 100, 205 | "nullPointMode": "connected", 206 | "nullText": null, 207 | "postfix": "", 208 | "postfixFontSize": "30%", 209 | "prefix": "", 210 | "prefixFontSize": "50%", 211 | "span": 2, 212 | "sparkline": { 213 | "fillColor": "rgba(31, 118, 189, 0.18)", 214 | "full": false, 215 | "lineColor": "rgb(31, 120, 193)", 216 | "show": true 217 | }, 218 | "targets": [ 219 | { 220 | "dsType": "influxdb", 221 | "groupBy": [], 222 | "measurement": "Ethernet_Interface_State", 223 | "policy": "default", 224 | "query": "SELECT last(\"value\") FROM \"Ethernet_Interface_State\" WHERE $timeFilter", 225 | "rawQuery": true, 226 | "refId": "A", 227 | "resultFormat": "time_series", 228 | "select": [ 229 | [ 230 | { 231 | "params": [ 232 | "value" 233 | ], 234 | "type": "field" 235 | } 236 | ] 237 | ], 238 | "tags": [] 239 | } 240 | ], 241 | "thresholds": "", 242 | "title": "Ethernet", 243 | "transparent": true, 244 | "type": "singlestat", 245 | "valueFontSize": "50%", 246 | "valueMaps": [ 247 | { 248 | "value": "null", 249 | "op": "=", 250 | "text": "N/A" 251 | } 252 | ], 253 | "valueName": "current" 254 | }, 255 | { 256 | "cacheTimeout": null, 257 | "colorBackground": false, 258 | "colorValue": false, 259 | "colors": [ 260 | "rgba(245, 54, 54, 0.9)", 261 | "rgba(237, 129, 40, 0.89)", 262 | "rgba(50, 172, 45, 0.97)" 263 | ], 264 | "datasource": "BeastCraft", 265 | "editable": true, 266 | "error": false, 267 | "format": "none", 268 | "gauge": { 269 | "maxValue": 100, 270 | "minValue": 0, 271 | "show": false, 272 | "thresholdLabels": false, 273 | "thresholdMarkers": true 274 | }, 275 | "height": "100px", 276 | "id": 11, 277 | "interval": null, 278 | "isNew": true, 279 | "links": [], 280 | "maxDataPoints": 100, 281 | "nullPointMode": "connected", 282 | "nullText": null, 283 | "postfix": "", 284 | "postfixFontSize": "30%", 285 | "prefix": "", 286 | "prefixFontSize": "50%", 287 | "span": 2, 288 | "sparkline": { 289 | "fillColor": "rgba(31, 118, 189, 0.18)", 290 | "full": false, 291 | "lineColor": "rgb(31, 120, 193)", 292 | "show": true 293 | }, 294 | "targets": [ 295 | { 296 | "dsType": "influxdb", 297 | "groupBy": [], 298 | "measurement": "Satellite_Interface_State", 299 | "policy": "default", 300 | "query": "SELECT last(\"value\") FROM \"Satellite_Interface_State\" WHERE $timeFilter", 301 | "rawQuery": true, 302 | "refId": "A", 303 | "resultFormat": "time_series", 304 | "select": [ 305 | [ 306 | { 307 | "params": [ 308 | "value" 309 | ], 310 | "type": "field" 311 | } 312 | ] 313 | ], 314 | "tags": [] 315 | } 316 | ], 317 | "thresholds": "", 318 | "title": "Satellite NIC", 319 | "transparent": true, 320 | "type": "singlestat", 321 | "valueFontSize": "50%", 322 | "valueMaps": [ 323 | { 324 | "value": "null", 325 | "op": "=", 326 | "text": "N/A" 327 | } 328 | ], 329 | "valueName": "current" 330 | }, 331 | { 332 | "cacheTimeout": null, 333 | "colorBackground": false, 334 | "colorValue": false, 335 | "colors": [ 336 | "rgba(245, 54, 54, 0.9)", 337 | "rgba(237, 129, 40, 0.89)", 338 | "rgba(50, 172, 45, 0.97)" 339 | ], 340 | "datasource": "BeastCraft", 341 | "editable": true, 342 | "error": false, 343 | "format": "none", 344 | "gauge": { 345 | "maxValue": 100, 346 | "minValue": 0, 347 | "show": false, 348 | "thresholdLabels": false, 349 | "thresholdMarkers": true 350 | }, 351 | "height": "100px", 352 | "id": 3, 353 | "interval": null, 354 | "isNew": true, 355 | "links": [], 356 | "maxDataPoints": 100, 357 | "nullPointMode": "connected", 358 | "nullText": null, 359 | "postfix": "", 360 | "postfixFontSize": "50%", 361 | "prefix": "", 362 | "prefixFontSize": "50%", 363 | "span": 2, 364 | "sparkline": { 365 | "fillColor": "rgba(31, 118, 189, 0.18)", 366 | "full": false, 367 | "lineColor": "rgb(31, 120, 193)", 368 | "show": true 369 | }, 370 | "targets": [ 371 | { 372 | "dsType": "influxdb", 373 | "groupBy": [], 374 | "measurement": "network_name", 375 | "policy": "default", 376 | "query": "SELECT last(\"value\") FROM \"network_name\" WHERE $timeFilter", 377 | "rawQuery": true, 378 | "refId": "A", 379 | "resultFormat": "time_series", 380 | "select": [ 381 | [ 382 | { 383 | "params": [ 384 | "value" 385 | ], 386 | "type": "field" 387 | } 388 | ] 389 | ], 390 | "tags": [] 391 | } 392 | ], 393 | "thresholds": "", 394 | "title": "Network", 395 | "transparent": true, 396 | "type": "singlestat", 397 | "valueFontSize": "50%", 398 | "valueMaps": [ 399 | { 400 | "value": "null", 401 | "op": "=", 402 | "text": "N/A" 403 | } 404 | ], 405 | "valueName": "current" 406 | } 407 | ], 408 | "title": "Stats" 409 | }, 410 | { 411 | "collapse": false, 412 | "editable": true, 413 | "height": "250px", 414 | "panels": [ 415 | { 416 | "cacheTimeout": null, 417 | "colorBackground": false, 418 | "colorValue": false, 419 | "colors": [ 420 | "rgba(245, 54, 54, 0.9)", 421 | "rgba(237, 129, 40, 0.89)", 422 | "rgba(50, 172, 45, 0.97)" 423 | ], 424 | "datasource": "BeastCraft", 425 | "decimals": 2, 426 | "editable": true, 427 | "error": false, 428 | "format": "none", 429 | "gauge": { 430 | "maxValue": 100, 431 | "minValue": 0, 432 | "show": false, 433 | "thresholdLabels": false, 434 | "thresholdMarkers": true 435 | }, 436 | "id": 9, 437 | "interval": null, 438 | "isNew": true, 439 | "links": [], 440 | "maxDataPoints": 100, 441 | "nullPointMode": "connected", 442 | "nullText": null, 443 | "postfix": "", 444 | "postfixFontSize": "50%", 445 | "prefix": "", 446 | "prefixFontSize": "50%", 447 | "span": 4, 448 | "sparkline": { 449 | "fillColor": "rgba(31, 118, 189, 0.18)", 450 | "full": false, 451 | "lineColor": "rgb(31, 120, 193)", 452 | "show": false 453 | }, 454 | "targets": [ 455 | { 456 | "dsType": "influxdb", 457 | "groupBy": [], 458 | "measurement": "signal_strength_dbm", 459 | "policy": "default", 460 | "query": "SELECT last(\"value\") FROM \"signal_strength_dbm\" WHERE $timeFilter", 461 | "rawQuery": true, 462 | "refId": "A", 463 | "resultFormat": "time_series", 464 | "select": [ 465 | [ 466 | { 467 | "params": [ 468 | "value" 469 | ], 470 | "type": "field" 471 | } 472 | ] 473 | ], 474 | "tags": [] 475 | } 476 | ], 477 | "thresholds": "", 478 | "title": "Signal Strength (dBm)", 479 | "type": "singlestat", 480 | "valueFontSize": "80%", 481 | "valueMaps": [ 482 | { 483 | "op": "=", 484 | "text": "N/A", 485 | "value": "null" 486 | } 487 | ], 488 | "valueName": "current", 489 | "transparent": true 490 | }, 491 | { 492 | "cacheTimeout": null, 493 | "colorBackground": false, 494 | "colorValue": false, 495 | "colors": [ 496 | "rgba(245, 54, 54, 0.9)", 497 | "rgba(237, 129, 40, 0.89)", 498 | "rgba(50, 172, 45, 0.97)" 499 | ], 500 | "datasource": "BeastCraft", 501 | "decimals": 2, 502 | "editable": true, 503 | "error": false, 504 | "format": "none", 505 | "gauge": { 506 | "maxValue": 100, 507 | "minValue": 0, 508 | "show": false, 509 | "thresholdLabels": false, 510 | "thresholdMarkers": true 511 | }, 512 | "id": 10, 513 | "interval": null, 514 | "isNew": true, 515 | "links": [], 516 | "maxDataPoints": 100, 517 | "nullPointMode": "connected", 518 | "nullText": null, 519 | "postfix": "%", 520 | "postfixFontSize": "50%", 521 | "prefix": "", 522 | "prefixFontSize": "50%", 523 | "span": 4, 524 | "sparkline": { 525 | "fillColor": "rgba(31, 118, 189, 0.18)", 526 | "full": false, 527 | "lineColor": "rgb(31, 120, 193)", 528 | "show": false 529 | }, 530 | "targets": [ 531 | { 532 | "dsType": "influxdb", 533 | "groupBy": [], 534 | "measurement": "signal_strength", 535 | "policy": "default", 536 | "query": "SELECT last(\"value\") FROM \"signal_strength\" WHERE $timeFilter", 537 | "rawQuery": true, 538 | "refId": "A", 539 | "resultFormat": "time_series", 540 | "select": [ 541 | [ 542 | { 543 | "params": [ 544 | "value" 545 | ], 546 | "type": "field" 547 | } 548 | ] 549 | ], 550 | "tags": [] 551 | } 552 | ], 553 | "thresholds": "", 554 | "title": "Signal Strength %", 555 | "type": "singlestat", 556 | "valueFontSize": "80%", 557 | "valueMaps": [ 558 | { 559 | "op": "=", 560 | "text": "N/A", 561 | "value": "null" 562 | } 563 | ], 564 | "valueName": "current", 565 | "transparent": true 566 | }, 567 | { 568 | "cacheTimeout": null, 569 | "colorBackground": false, 570 | "colorValue": false, 571 | "colors": [ 572 | "rgba(245, 54, 54, 0.9)", 573 | "rgba(237, 129, 40, 0.89)", 574 | "rgba(50, 172, 45, 0.97)" 575 | ], 576 | "datasource": "BeastCraft", 577 | "decimals": 2, 578 | "editable": true, 579 | "error": false, 580 | "format": "none", 581 | "gauge": { 582 | "maxValue": 100, 583 | "minValue": 0, 584 | "show": false, 585 | "thresholdLabels": false, 586 | "thresholdMarkers": true 587 | }, 588 | "id": 12, 589 | "interval": null, 590 | "isNew": true, 591 | "links": [], 592 | "maxDataPoints": 100, 593 | "nullPointMode": "connected", 594 | "nullText": null, 595 | "postfix": "", 596 | "postfixFontSize": "50%", 597 | "prefix": "", 598 | "prefixFontSize": "50%", 599 | "span": 4, 600 | "sparkline": { 601 | "fillColor": "rgba(31, 118, 189, 0.18)", 602 | "full": false, 603 | "lineColor": "rgb(31, 120, 193)", 604 | "show": false 605 | }, 606 | "targets": [ 607 | { 608 | "dsType": "influxdb", 609 | "groupBy": [], 610 | "measurement": "orbital_position", 611 | "policy": "default", 612 | "query": "SELECT last(\"value\") FROM \"orbital_position\" WHERE $timeFilter", 613 | "rawQuery": true, 614 | "refId": "A", 615 | "resultFormat": "time_series", 616 | "select": [ 617 | [ 618 | { 619 | "params": [ 620 | "value" 621 | ], 622 | "type": "field" 623 | } 624 | ] 625 | ], 626 | "tags": [] 627 | } 628 | ], 629 | "thresholds": "", 630 | "title": "Orbital Position", 631 | "type": "singlestat", 632 | "valueFontSize": "80%", 633 | "valueMaps": [ 634 | { 635 | "op": "=", 636 | "text": "N/A", 637 | "value": "null" 638 | } 639 | ], 640 | "valueName": "current", 641 | "transparent": true 642 | } 643 | ], 644 | "title": "Graphs" 645 | } 646 | ], 647 | "time": { 648 | "from": "now-1h", 649 | "to": "now" 650 | }, 651 | "timepicker": { 652 | "now": true, 653 | "refresh_intervals": [ 654 | "5s", 655 | "10s", 656 | "30s", 657 | "1m", 658 | "5m", 659 | "15m", 660 | "30m", 661 | "1h", 662 | "2h", 663 | "1d" 664 | ], 665 | "time_options": [ 666 | "5m", 667 | "15m", 668 | "1h", 669 | "6h", 670 | "12h", 671 | "24h", 672 | "2d", 673 | "7d", 674 | "30d" 675 | ] 676 | }, 677 | "templating": { 678 | "list": [] 679 | }, 680 | "annotations": { 681 | "list": [] 682 | }, 683 | "refresh": "1m", 684 | "schemaVersion": 12, 685 | "version": 12, 686 | "links": [] 687 | } 688 | -------------------------------------------------------------------------------- /mobilebroadband.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 5, 3 | "title": "Mobile Broadband", 4 | "originalTitle": "Mobile Broadband", 5 | "tags": [ 6 | "mobile", 7 | "broadband" 8 | ], 9 | "style": "dark", 10 | "timezone": "browser", 11 | "editable": true, 12 | "hideControls": false, 13 | "sharedCrosshair": false, 14 | "rows": [ 15 | { 16 | "collapse": false, 17 | "editable": true, 18 | "height": "0", 19 | "panels": [ 20 | { 21 | "cacheTimeout": null, 22 | "colorBackground": false, 23 | "colorValue": false, 24 | "colors": [ 25 | "rgba(245, 54, 54, 0.9)", 26 | "rgba(237, 129, 40, 0.89)", 27 | "rgba(50, 172, 45, 0.97)" 28 | ], 29 | "datasource": "BeastCraft", 30 | "editable": true, 31 | "error": false, 32 | "format": "none", 33 | "gauge": { 34 | "maxValue": 100, 35 | "minValue": 0, 36 | "show": false, 37 | "thresholdLabels": false, 38 | "thresholdMarkers": true 39 | }, 40 | "height": "100px", 41 | "id": 1, 42 | "interval": null, 43 | "isNew": true, 44 | "links": [ 45 | { 46 | "includeVars": false, 47 | "targetBlank": true, 48 | "title": "State", 49 | "type": "absolute", 50 | "url": "http://dash.beastcraft.belodedenko.me/goform/goform_get_cmd_process?multi_data=1&isTest=false&sms_received_flag_flag=0&sts_received_flag_flag=0&cmd=modem_main_state%2Cpin_status%2Cloginfo%2Cnew_version_state%2Ccurrent_upgrade_state%2Cis_mandatory%2Csms_received_flag%2Csts_received_flag%2Csignalbar%2Cnetwork_type%2Cnetwork_provider%2Cppp_status%2CEX_SSID1%2Cex_wifi_status%2CEX_wifi_profile%2Cm_ssid_enable%2Csms_unread_num%2CRadioOff%2Csimcard_roam%2Clan_ipaddr%2Cstation_mac%2Cbattery_charging%2Cbattery_vol_percent%2Cbattery_pers%2Cspn_display_flag%2Cplmn_display_flag%2Cspn_name_data%2Cspn_b1_flag%2Cspn_b2_flag%2Crealtime_tx_bytes%2Crealtime_rx_bytes%2Crealtime_time%2Crealtime_tx_thrpt%2Crealtime_rx_thrpt%2Cmonthly_rx_bytes%2Cmonthly_tx_bytes%2Cmonthly_time%2Cdate_month%2Cdata_volume_limit_switch%2Cdata_volume_limit_size%2Cdata_volume_alert_percent%2Cdata_volume_limit_unit%2Croam_setting_option%2Cupg_roam_switch%2Chplmn" 51 | }, 52 | { 53 | "dashboard": "http://beastpi3.local.belodedenko.me/goform//goform_set_cmd_process?isTest=false¬Callback=true&goformId=CONNECT_NETWORK", 54 | "targetBlank": true, 55 | "title": "Connect", 56 | "type": "absolute", 57 | "url": "http://dash.beastcraft.belodedenko.me/goform//goform_set_cmd_process?isTest=false¬Callback=true&goformId=CONNECT_NETWORK" 58 | }, 59 | { 60 | "targetBlank": true, 61 | "title": "Disconnect", 62 | "type": "absolute", 63 | "url": "http://dash.beastcraft.belodedenko.me/goform//goform_set_cmd_process?isTest=false¬Callback=true&goformId=DISCONNECT_NETWORK" 64 | }, 65 | { 66 | "targetBlank": true, 67 | "title": "Router", 68 | "type": "absolute", 69 | "url": "http://beastroute1-web.beastcraft.belodedenko.me/" 70 | }, 71 | { 72 | "dashboard": "http:/172.17.0.1/", 73 | "targetBlank": true, 74 | "title": "Modem", 75 | "type": "absolute", 76 | "url": "http://mf823-web.beastcraft.belodedenko.me/index.html" 77 | } 78 | ], 79 | "maxDataPoints": 100, 80 | "nullPointMode": "connected", 81 | "nullText": null, 82 | "postfix": "", 83 | "postfixFontSize": "50%", 84 | "prefix": "", 85 | "prefixFontSize": "50%", 86 | "span": 3, 87 | "sparkline": { 88 | "fillColor": "rgba(31, 118, 189, 0.18)", 89 | "full": false, 90 | "lineColor": "rgb(31, 120, 193)", 91 | "show": false 92 | }, 93 | "targets": [ 94 | { 95 | "dsType": "influxdb", 96 | "groupBy": [], 97 | "measurement": "ppp_status", 98 | "policy": "default", 99 | "query": "SELECT last(\"value\") FROM \"ppp_status\" WHERE $timeFilter", 100 | "rawQuery": true, 101 | "refId": "A", 102 | "resultFormat": "time_series", 103 | "select": [ 104 | [ 105 | { 106 | "params": [ 107 | "value" 108 | ], 109 | "type": "field" 110 | } 111 | ] 112 | ], 113 | "tags": [] 114 | } 115 | ], 116 | "thresholds": "", 117 | "title": "Status", 118 | "transparent": true, 119 | "type": "singlestat", 120 | "valueFontSize": "80%", 121 | "valueMaps": [], 122 | "valueName": "current" 123 | }, 124 | { 125 | "cacheTimeout": null, 126 | "colorBackground": false, 127 | "colorValue": false, 128 | "colors": [ 129 | "rgba(245, 54, 54, 0.9)", 130 | "rgba(237, 129, 40, 0.89)", 131 | "rgba(50, 172, 45, 0.97)" 132 | ], 133 | "datasource": "BeastCraft", 134 | "editable": true, 135 | "error": false, 136 | "format": "none", 137 | "gauge": { 138 | "maxValue": 100, 139 | "minValue": 0, 140 | "show": false, 141 | "thresholdLabels": false, 142 | "thresholdMarkers": true 143 | }, 144 | "height": "100px", 145 | "id": 8, 146 | "interval": null, 147 | "isNew": true, 148 | "links": [ 149 | { 150 | "includeVars": false, 151 | "params": "", 152 | "targetBlank": true, 153 | "title": "Manual", 154 | "type": "absolute", 155 | "url": "http://dash.beastcraft.belodedenko.me/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&ConnectionMode=manual_dial" 156 | }, 157 | { 158 | "params": "", 159 | "targetBlank": true, 160 | "title": "Auto", 161 | "type": "absolute", 162 | "url": "http://dash.beastcraft.belodedenko.me/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&ConnectionMode=auto_dial" 163 | } 164 | ], 165 | "maxDataPoints": 100, 166 | "nullPointMode": "connected", 167 | "nullText": null, 168 | "postfix": "", 169 | "postfixFontSize": "50%", 170 | "prefix": "", 171 | "prefixFontSize": "50%", 172 | "span": 2, 173 | "sparkline": { 174 | "fillColor": "rgba(31, 118, 189, 0.18)", 175 | "full": false, 176 | "lineColor": "rgb(31, 120, 193)", 177 | "show": true 178 | }, 179 | "targets": [ 180 | { 181 | "dsType": "influxdb", 182 | "groupBy": [], 183 | "measurement": "connectionMode", 184 | "policy": "default", 185 | "query": "SELECT last(\"value\") FROM \"connectionMode\" WHERE $timeFilter", 186 | "rawQuery": true, 187 | "refId": "A", 188 | "resultFormat": "time_series", 189 | "select": [ 190 | [ 191 | { 192 | "params": [ 193 | "value" 194 | ], 195 | "type": "field" 196 | } 197 | ] 198 | ], 199 | "tags": [] 200 | } 201 | ], 202 | "thresholds": "", 203 | "title": "Mode", 204 | "transparent": true, 205 | "type": "singlestat", 206 | "valueFontSize": "50%", 207 | "valueMaps": [], 208 | "valueName": "current" 209 | }, 210 | { 211 | "cacheTimeout": null, 212 | "colorBackground": false, 213 | "colorValue": true, 214 | "colors": [ 215 | "rgba(245, 54, 54, 0.9)", 216 | "rgba(237, 129, 40, 0.89)", 217 | "rgba(50, 172, 45, 0.97)" 218 | ], 219 | "datasource": "BeastCraft", 220 | "editable": true, 221 | "error": false, 222 | "format": "none", 223 | "gauge": { 224 | "maxValue": 100, 225 | "minValue": 0, 226 | "show": false, 227 | "thresholdLabels": false, 228 | "thresholdMarkers": true 229 | }, 230 | "height": "100px", 231 | "id": 2, 232 | "interval": null, 233 | "isNew": true, 234 | "links": [], 235 | "maxDataPoints": 100, 236 | "nullPointMode": "connected", 237 | "nullText": null, 238 | "postfix": " / 5", 239 | "postfixFontSize": "30%", 240 | "prefix": "", 241 | "prefixFontSize": "50%", 242 | "span": 2, 243 | "sparkline": { 244 | "fillColor": "rgba(31, 118, 189, 0.18)", 245 | "full": false, 246 | "lineColor": "rgb(31, 120, 193)", 247 | "show": true 248 | }, 249 | "targets": [ 250 | { 251 | "dsType": "influxdb", 252 | "groupBy": [], 253 | "measurement": "signalbar", 254 | "policy": "default", 255 | "query": "SELECT \"value\" FROM \"signalbar\" WHERE $timeFilter", 256 | "rawQuery": true, 257 | "refId": "A", 258 | "resultFormat": "time_series", 259 | "select": [ 260 | [ 261 | { 262 | "params": [ 263 | "value" 264 | ], 265 | "type": "field" 266 | } 267 | ] 268 | ], 269 | "tags": [] 270 | } 271 | ], 272 | "thresholds": "2,3", 273 | "title": "Signal", 274 | "transparent": true, 275 | "type": "singlestat", 276 | "valueFontSize": "50%", 277 | "valueMaps": [], 278 | "valueName": "current" 279 | }, 280 | { 281 | "cacheTimeout": null, 282 | "colorBackground": false, 283 | "colorValue": false, 284 | "colors": [ 285 | "rgba(245, 54, 54, 0.9)", 286 | "rgba(237, 129, 40, 0.89)", 287 | "rgba(50, 172, 45, 0.97)" 288 | ], 289 | "datasource": "BeastCraft", 290 | "editable": true, 291 | "error": false, 292 | "format": "none", 293 | "gauge": { 294 | "maxValue": 100, 295 | "minValue": 0, 296 | "show": false, 297 | "thresholdLabels": false, 298 | "thresholdMarkers": true 299 | }, 300 | "height": "100px", 301 | "id": 4, 302 | "interval": null, 303 | "isNew": true, 304 | "links": [], 305 | "maxDataPoints": 100, 306 | "nullPointMode": "connected", 307 | "nullText": null, 308 | "postfix": "", 309 | "postfixFontSize": "50%", 310 | "prefix": "", 311 | "prefixFontSize": "50%", 312 | "span": 2, 313 | "sparkline": { 314 | "fillColor": "rgba(31, 118, 189, 0.18)", 315 | "full": false, 316 | "lineColor": "rgb(31, 120, 193)", 317 | "show": true 318 | }, 319 | "targets": [ 320 | { 321 | "dsType": "influxdb", 322 | "groupBy": [], 323 | "measurement": "network_type", 324 | "policy": "default", 325 | "query": "SELECT last(\"value\") FROM \"network_type\" WHERE $timeFilter", 326 | "rawQuery": true, 327 | "refId": "A", 328 | "resultFormat": "time_series", 329 | "select": [ 330 | [ 331 | { 332 | "params": [ 333 | "value" 334 | ], 335 | "type": "field" 336 | } 337 | ] 338 | ], 339 | "tags": [] 340 | } 341 | ], 342 | "thresholds": "", 343 | "title": "Type", 344 | "transparent": true, 345 | "type": "singlestat", 346 | "valueFontSize": "50%", 347 | "valueMaps": [], 348 | "valueName": "current" 349 | }, 350 | { 351 | "cacheTimeout": null, 352 | "colorBackground": false, 353 | "colorValue": false, 354 | "colors": [ 355 | "rgba(245, 54, 54, 0.9)", 356 | "rgba(237, 129, 40, 0.89)", 357 | "rgba(50, 172, 45, 0.97)" 358 | ], 359 | "datasource": "BeastCraft", 360 | "editable": true, 361 | "error": false, 362 | "format": "none", 363 | "gauge": { 364 | "maxValue": 100, 365 | "minValue": 0, 366 | "show": false, 367 | "thresholdLabels": false, 368 | "thresholdMarkers": true 369 | }, 370 | "height": "100px", 371 | "id": 3, 372 | "interval": null, 373 | "isNew": true, 374 | "links": [], 375 | "maxDataPoints": 100, 376 | "nullPointMode": "connected", 377 | "nullText": null, 378 | "postfix": "", 379 | "postfixFontSize": "50%", 380 | "prefix": "", 381 | "prefixFontSize": "50%", 382 | "span": 2, 383 | "sparkline": { 384 | "fillColor": "rgba(31, 118, 189, 0.18)", 385 | "full": false, 386 | "lineColor": "rgb(31, 120, 193)", 387 | "show": true 388 | }, 389 | "targets": [ 390 | { 391 | "dsType": "influxdb", 392 | "groupBy": [], 393 | "measurement": "network_provider", 394 | "policy": "default", 395 | "query": "SELECT last(\"value\") FROM \"network_provider\" WHERE $timeFilter", 396 | "rawQuery": true, 397 | "refId": "A", 398 | "resultFormat": "time_series", 399 | "select": [ 400 | [ 401 | { 402 | "params": [ 403 | "value" 404 | ], 405 | "type": "field" 406 | } 407 | ] 408 | ], 409 | "tags": [] 410 | } 411 | ], 412 | "thresholds": "", 413 | "title": "Network", 414 | "transparent": true, 415 | "type": "singlestat", 416 | "valueFontSize": "50%", 417 | "valueMaps": [], 418 | "valueName": "current" 419 | }, 420 | { 421 | "cacheTimeout": null, 422 | "colorBackground": false, 423 | "colorValue": false, 424 | "colors": [ 425 | "rgba(245, 54, 54, 0.9)", 426 | "rgba(237, 129, 40, 0.89)", 427 | "rgba(50, 172, 45, 0.97)" 428 | ], 429 | "datasource": "BeastCraft", 430 | "editable": true, 431 | "error": false, 432 | "format": "none", 433 | "gauge": { 434 | "maxValue": 100, 435 | "minValue": 0, 436 | "show": false, 437 | "thresholdLabels": false, 438 | "thresholdMarkers": true 439 | }, 440 | "height": "100px", 441 | "id": 6, 442 | "interval": null, 443 | "isNew": true, 444 | "links": [ 445 | { 446 | "params": "", 447 | "targetBlank": true, 448 | "title": "On", 449 | "type": "absolute", 450 | "url": "http://dash.beastcraft.belodedenko.me/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&roam_setting_option=on" 451 | }, 452 | { 453 | "params": "", 454 | "targetBlank": true, 455 | "title": "Off", 456 | "type": "absolute", 457 | "url": "http://dash.beastcraft.belodedenko.me/goform/goform_set_cmd_process?isTest=false¬Callback=true&goformId=SET_CONNECTION_MODE&roam_setting_option=off" 458 | } 459 | ], 460 | "maxDataPoints": 100, 461 | "nullPointMode": "connected", 462 | "nullText": null, 463 | "postfix": "", 464 | "postfixFontSize": "50%", 465 | "prefix": "", 466 | "prefixFontSize": "50%", 467 | "span": 1, 468 | "sparkline": { 469 | "fillColor": "rgba(31, 118, 189, 0.18)", 470 | "full": false, 471 | "lineColor": "rgb(31, 120, 193)", 472 | "show": true 473 | }, 474 | "targets": [ 475 | { 476 | "dsType": "influxdb", 477 | "groupBy": [], 478 | "measurement": "roam_setting_option", 479 | "policy": "default", 480 | "query": "SELECT last(\"value\") FROM \"roam_setting_option\" WHERE $timeFilter", 481 | "rawQuery": true, 482 | "refId": "A", 483 | "resultFormat": "time_series", 484 | "select": [ 485 | [ 486 | { 487 | "params": [ 488 | "value" 489 | ], 490 | "type": "field" 491 | } 492 | ] 493 | ], 494 | "tags": [] 495 | } 496 | ], 497 | "thresholds": "", 498 | "title": "Roam", 499 | "transparent": true, 500 | "type": "singlestat", 501 | "valueFontSize": "50%", 502 | "valueMaps": [], 503 | "valueName": "current" 504 | } 505 | ], 506 | "title": "Stats" 507 | }, 508 | { 509 | "collapse": false, 510 | "editable": true, 511 | "height": "250px", 512 | "panels": [ 513 | { 514 | "aliasColors": {}, 515 | "bars": false, 516 | "datasource": "BeastCraft", 517 | "editable": true, 518 | "error": false, 519 | "fill": 1, 520 | "grid": { 521 | "threshold1": null, 522 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 523 | "threshold2": null, 524 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 525 | }, 526 | "id": 5, 527 | "isNew": true, 528 | "legend": { 529 | "avg": false, 530 | "current": false, 531 | "max": false, 532 | "min": false, 533 | "show": false, 534 | "total": false, 535 | "values": false 536 | }, 537 | "lines": true, 538 | "linewidth": 2, 539 | "links": [], 540 | "nullPointMode": "connected", 541 | "percentage": false, 542 | "pointradius": 5, 543 | "points": false, 544 | "renderer": "flot", 545 | "seriesOverrides": [], 546 | "span": 6, 547 | "stack": false, 548 | "steppedLine": false, 549 | "targets": [ 550 | { 551 | "dsType": "influxdb", 552 | "groupBy": [ 553 | { 554 | "params": [ 555 | "$interval" 556 | ], 557 | "type": "time" 558 | }, 559 | { 560 | "params": [ 561 | "null" 562 | ], 563 | "type": "fill" 564 | } 565 | ], 566 | "measurement": "signalbar", 567 | "policy": "default", 568 | "query": "SELECT last(\"value\") FROM \"signalbar\" WHERE $timeFilter GROUP BY time($interval) fill(null)", 569 | "refId": "A", 570 | "resultFormat": "time_series", 571 | "select": [ 572 | [ 573 | { 574 | "params": [ 575 | "value" 576 | ], 577 | "type": "field" 578 | }, 579 | { 580 | "params": [], 581 | "type": "last" 582 | } 583 | ] 584 | ], 585 | "tags": [] 586 | } 587 | ], 588 | "timeFrom": null, 589 | "timeShift": null, 590 | "title": "Signal", 591 | "tooltip": { 592 | "msResolution": false, 593 | "shared": true, 594 | "value_type": "cumulative" 595 | }, 596 | "transparent": true, 597 | "type": "graph", 598 | "xaxis": { 599 | "show": true 600 | }, 601 | "yaxes": [ 602 | { 603 | "format": "short", 604 | "label": "bars", 605 | "logBase": 1, 606 | "max": 5, 607 | "min": 0, 608 | "show": true 609 | }, 610 | { 611 | "format": "short", 612 | "logBase": 1, 613 | "max": null, 614 | "min": null, 615 | "show": true 616 | } 617 | ] 618 | }, 619 | { 620 | "cacheTimeout": null, 621 | "colorBackground": false, 622 | "colorValue": false, 623 | "colors": [ 624 | "rgba(245, 54, 54, 0.9)", 625 | "rgba(237, 129, 40, 0.89)", 626 | "rgba(50, 172, 45, 0.97)" 627 | ], 628 | "datasource": "BeastCraft", 629 | "decimals": 2, 630 | "editable": true, 631 | "error": false, 632 | "format": "bytes", 633 | "gauge": { 634 | "maxValue": 100, 635 | "minValue": 0, 636 | "show": false, 637 | "thresholdLabels": false, 638 | "thresholdMarkers": true 639 | }, 640 | "id": 9, 641 | "interval": null, 642 | "isNew": true, 643 | "links": [], 644 | "maxDataPoints": 100, 645 | "nullPointMode": "connected", 646 | "nullText": null, 647 | "postfix": "", 648 | "postfixFontSize": "50%", 649 | "prefix": "", 650 | "prefixFontSize": "50%", 651 | "span": 3, 652 | "sparkline": { 653 | "fillColor": "rgba(31, 118, 189, 0.18)", 654 | "full": false, 655 | "lineColor": "rgb(31, 120, 193)", 656 | "show": false 657 | }, 658 | "targets": [ 659 | { 660 | "dsType": "influxdb", 661 | "groupBy": [], 662 | "measurement": "monthly_rx_bytes", 663 | "policy": "default", 664 | "query": "SELECT last(\"value\") FROM \"monthly_rx_bytes\" WHERE $timeFilter", 665 | "rawQuery": true, 666 | "refId": "A", 667 | "resultFormat": "time_series", 668 | "select": [ 669 | [ 670 | { 671 | "params": [ 672 | "value" 673 | ], 674 | "type": "field" 675 | } 676 | ] 677 | ], 678 | "tags": [] 679 | } 680 | ], 681 | "thresholds": "", 682 | "title": "Monthly RX", 683 | "type": "singlestat", 684 | "valueFontSize": "80%", 685 | "valueMaps": [ 686 | { 687 | "op": "=", 688 | "text": "N/A", 689 | "value": "null" 690 | } 691 | ], 692 | "valueName": "current" 693 | }, 694 | { 695 | "cacheTimeout": null, 696 | "colorBackground": false, 697 | "colorValue": false, 698 | "colors": [ 699 | "rgba(245, 54, 54, 0.9)", 700 | "rgba(237, 129, 40, 0.89)", 701 | "rgba(50, 172, 45, 0.97)" 702 | ], 703 | "datasource": "BeastCraft", 704 | "decimals": 2, 705 | "editable": true, 706 | "error": false, 707 | "format": "bytes", 708 | "gauge": { 709 | "maxValue": 100, 710 | "minValue": 0, 711 | "show": false, 712 | "thresholdLabels": false, 713 | "thresholdMarkers": true 714 | }, 715 | "id": 10, 716 | "interval": null, 717 | "isNew": true, 718 | "links": [], 719 | "maxDataPoints": 100, 720 | "nullPointMode": "connected", 721 | "nullText": null, 722 | "postfix": "", 723 | "postfixFontSize": "50%", 724 | "prefix": "", 725 | "prefixFontSize": "50%", 726 | "span": 3, 727 | "sparkline": { 728 | "fillColor": "rgba(31, 118, 189, 0.18)", 729 | "full": false, 730 | "lineColor": "rgb(31, 120, 193)", 731 | "show": false 732 | }, 733 | "targets": [ 734 | { 735 | "dsType": "influxdb", 736 | "groupBy": [], 737 | "measurement": "monthly_tx_bytes", 738 | "policy": "default", 739 | "query": "SELECT last(\"value\") FROM \"monthly_tx_bytes\" WHERE $timeFilter", 740 | "rawQuery": true, 741 | "refId": "A", 742 | "resultFormat": "time_series", 743 | "select": [ 744 | [ 745 | { 746 | "params": [ 747 | "value" 748 | ], 749 | "type": "field" 750 | } 751 | ] 752 | ], 753 | "tags": [] 754 | } 755 | ], 756 | "thresholds": "", 757 | "title": "Monthly TX", 758 | "type": "singlestat", 759 | "valueFontSize": "80%", 760 | "valueMaps": [ 761 | { 762 | "op": "=", 763 | "text": "N/A", 764 | "value": "null" 765 | } 766 | ], 767 | "valueName": "current" 768 | } 769 | ], 770 | "title": "Graphs" 771 | } 772 | ], 773 | "time": { 774 | "from": "now-1h", 775 | "to": "now" 776 | }, 777 | "timepicker": { 778 | "now": true, 779 | "refresh_intervals": [ 780 | "5s", 781 | "10s", 782 | "30s", 783 | "1m", 784 | "5m", 785 | "15m", 786 | "30m", 787 | "1h", 788 | "2h", 789 | "1d" 790 | ], 791 | "time_options": [ 792 | "5m", 793 | "15m", 794 | "1h", 795 | "6h", 796 | "12h", 797 | "24h", 798 | "2d", 799 | "7d", 800 | "30d" 801 | ] 802 | }, 803 | "templating": { 804 | "list": [] 805 | }, 806 | "annotations": { 807 | "list": [] 808 | }, 809 | "refresh": "1m", 810 | "schemaVersion": 12, 811 | "version": 13, 812 | "links": [] 813 | } 814 | -------------------------------------------------------------------------------- /ups.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 3, 3 | "title": "Power", 4 | "originalTitle": "Power", 5 | "tags": [ 6 | "power" 7 | ], 8 | "style": "dark", 9 | "timezone": "browser", 10 | "editable": true, 11 | "hideControls": false, 12 | "sharedCrosshair": false, 13 | "rows": [ 14 | { 15 | "collapse": false, 16 | "editable": true, 17 | "height": "0", 18 | "panels": [ 19 | { 20 | "cacheTimeout": null, 21 | "colorBackground": false, 22 | "colorValue": false, 23 | "colors": [ 24 | "rgba(50, 172, 45, 0.97)", 25 | "rgba(237, 129, 40, 0.89)", 26 | "rgba(245, 54, 54, 0.9)" 27 | ], 28 | "datasource": "BeastCraft", 29 | "decimals": null, 30 | "editable": true, 31 | "error": false, 32 | "format": "none", 33 | "gauge": { 34 | "maxValue": 100, 35 | "minValue": 0, 36 | "show": false, 37 | "thresholdLabels": false, 38 | "thresholdMarkers": true 39 | }, 40 | "height": "0", 41 | "id": 8, 42 | "interval": null, 43 | "isNew": true, 44 | "links": [], 45 | "maxDataPoints": 100, 46 | "nullPointMode": "connected", 47 | "nullText": null, 48 | "postfix": "", 49 | "postfixFontSize": "50%", 50 | "prefix": "", 51 | "prefixFontSize": "50%", 52 | "span": 3, 53 | "sparkline": { 54 | "fillColor": "rgba(31, 118, 189, 0.18)", 55 | "full": false, 56 | "lineColor": "rgb(31, 120, 193)", 57 | "show": false 58 | }, 59 | "targets": [ 60 | { 61 | "alias": "", 62 | "dsType": "influxdb", 63 | "groupBy": [], 64 | "measurement": "ups.status", 65 | "policy": "default", 66 | "query": "SELECT last(\"value\") FROM \"ups.status\" WHERE $timeFilter", 67 | "rawQuery": true, 68 | "refId": "A", 69 | "resultFormat": "time_series", 70 | "select": [ 71 | [ 72 | { 73 | "params": [ 74 | "value" 75 | ], 76 | "type": "field" 77 | } 78 | ] 79 | ], 80 | "tags": [] 81 | } 82 | ], 83 | "thresholds": "", 84 | "title": "ups.status", 85 | "transparent": true, 86 | "type": "singlestat", 87 | "valueFontSize": "50%", 88 | "valueMaps": [ 89 | { 90 | "op": "=", 91 | "text": "N/A", 92 | "value": "null" 93 | } 94 | ], 95 | "valueName": "current" 96 | }, 97 | { 98 | "cacheTimeout": null, 99 | "colorBackground": false, 100 | "colorValue": true, 101 | "colors": [ 102 | "rgba(245, 54, 54, 0.9)", 103 | "rgba(237, 129, 40, 0.89)", 104 | "rgba(50, 172, 45, 0.97)" 105 | ], 106 | "datasource": "BeastCraft", 107 | "decimals": 1, 108 | "editable": true, 109 | "error": false, 110 | "format": "s", 111 | "gauge": { 112 | "maxValue": 100, 113 | "minValue": 0, 114 | "show": false, 115 | "thresholdLabels": false, 116 | "thresholdMarkers": true 117 | }, 118 | "height": "100px", 119 | "id": 13, 120 | "interval": null, 121 | "isNew": true, 122 | "links": [], 123 | "maxDataPoints": 100, 124 | "nullPointMode": "connected", 125 | "nullText": null, 126 | "postfix": "", 127 | "postfixFontSize": "50%", 128 | "prefix": "", 129 | "prefixFontSize": "50%", 130 | "span": 3, 131 | "sparkline": { 132 | "fillColor": "rgba(31, 118, 189, 0.18)", 133 | "full": false, 134 | "lineColor": "rgb(31, 120, 193)", 135 | "show": true 136 | }, 137 | "targets": [ 138 | { 139 | "alias": "", 140 | "dsType": "influxdb", 141 | "groupBy": [], 142 | "measurement": "battery.runtime", 143 | "policy": "default", 144 | "query": "SELECT \"value\" FROM \"battery.runtime\" WHERE $timeFilter", 145 | "rawQuery": true, 146 | "refId": "A", 147 | "resultFormat": "time_series", 148 | "select": [ 149 | [ 150 | { 151 | "params": [ 152 | "value" 153 | ], 154 | "type": "field" 155 | } 156 | ] 157 | ], 158 | "tags": [] 159 | } 160 | ], 161 | "thresholds": "300,900", 162 | "title": "battery.runtime", 163 | "transparent": true, 164 | "type": "singlestat", 165 | "valueFontSize": "50%", 166 | "valueMaps": [ 167 | { 168 | "op": "=", 169 | "text": "N/A", 170 | "value": "null" 171 | } 172 | ], 173 | "valueName": "current" 174 | }, 175 | { 176 | "cacheTimeout": null, 177 | "colorBackground": false, 178 | "colorValue": true, 179 | "colors": [ 180 | "rgba(245, 54, 54, 0.9)", 181 | "rgba(237, 129, 40, 0.89)", 182 | "rgba(50, 172, 45, 0.97)" 183 | ], 184 | "datasource": "BeastCraft", 185 | "decimals": 2, 186 | "editable": true, 187 | "error": false, 188 | "format": "volt", 189 | "gauge": { 190 | "maxValue": 100, 191 | "minValue": 0, 192 | "show": false, 193 | "thresholdLabels": false, 194 | "thresholdMarkers": true 195 | }, 196 | "height": "100px", 197 | "id": 2, 198 | "interval": null, 199 | "isNew": true, 200 | "links": [], 201 | "maxDataPoints": 100, 202 | "nullPointMode": "connected", 203 | "nullText": null, 204 | "postfix": "", 205 | "postfixFontSize": "50%", 206 | "prefix": "", 207 | "prefixFontSize": "50%", 208 | "span": 2, 209 | "sparkline": { 210 | "fillColor": "rgba(31, 118, 189, 0.18)", 211 | "full": true, 212 | "lineColor": "rgb(31, 120, 193)", 213 | "show": true 214 | }, 215 | "targets": [ 216 | { 217 | "dsType": "influxdb", 218 | "groupBy": [], 219 | "measurement": "battery.voltage", 220 | "policy": "default", 221 | "query": "SELECT \"value\" FROM \"battery.voltage\" WHERE $timeFilter", 222 | "rawQuery": true, 223 | "refId": "A", 224 | "resultFormat": "time_series", 225 | "select": [ 226 | [ 227 | { 228 | "params": [ 229 | "value" 230 | ], 231 | "type": "field" 232 | } 233 | ] 234 | ], 235 | "tags": [] 236 | } 237 | ], 238 | "thresholds": "11.31,11.9", 239 | "title": "battery.voltage", 240 | "transparent": true, 241 | "type": "singlestat", 242 | "valueFontSize": "50%", 243 | "valueMaps": [ 244 | { 245 | "op": "=", 246 | "text": "N/A", 247 | "value": "null" 248 | } 249 | ], 250 | "valueName": "current" 251 | }, 252 | { 253 | "cacheTimeout": null, 254 | "colorBackground": false, 255 | "colorValue": true, 256 | "colors": [ 257 | "rgba(245, 54, 54, 0.9)", 258 | "rgba(237, 129, 40, 0.89)", 259 | "rgba(50, 172, 45, 0.97)" 260 | ], 261 | "datasource": "BeastCraft", 262 | "decimals": null, 263 | "editable": true, 264 | "error": false, 265 | "format": "percent", 266 | "gauge": { 267 | "maxValue": 100, 268 | "minValue": 0, 269 | "show": false, 270 | "thresholdLabels": false, 271 | "thresholdMarkers": true 272 | }, 273 | "height": "100px", 274 | "id": 1, 275 | "interval": null, 276 | "isNew": true, 277 | "links": [], 278 | "maxDataPoints": 100, 279 | "nullPointMode": "connected", 280 | "nullText": null, 281 | "postfix": "", 282 | "postfixFontSize": "50%", 283 | "prefix": "", 284 | "prefixFontSize": "50%", 285 | "span": 2, 286 | "sparkline": { 287 | "fillColor": "rgba(31, 118, 189, 0.18)", 288 | "full": true, 289 | "lineColor": "rgb(31, 120, 193)", 290 | "show": true 291 | }, 292 | "targets": [ 293 | { 294 | "dsType": "influxdb", 295 | "groupBy": [], 296 | "measurement": "battery.charge", 297 | "policy": "default", 298 | "query": "SELECT \"value\" FROM \"battery.charge\" WHERE $timeFilter", 299 | "rawQuery": true, 300 | "refId": "A", 301 | "resultFormat": "time_series", 302 | "select": [ 303 | [ 304 | { 305 | "params": [ 306 | "value" 307 | ], 308 | "type": "field" 309 | } 310 | ] 311 | ], 312 | "tags": [] 313 | } 314 | ], 315 | "thresholds": "10,40", 316 | "title": "battery.charge", 317 | "transparent": true, 318 | "type": "singlestat", 319 | "valueFontSize": "80%", 320 | "valueMaps": [ 321 | { 322 | "op": "=", 323 | "text": "N/A", 324 | "value": "null" 325 | } 326 | ], 327 | "valueName": "current" 328 | }, 329 | { 330 | "cacheTimeout": null, 331 | "colorBackground": false, 332 | "colorValue": true, 333 | "colors": [ 334 | "rgba(50, 172, 45, 0.97)", 335 | "rgba(237, 129, 40, 0.89)", 336 | "rgba(245, 54, 54, 0.9)" 337 | ], 338 | "datasource": "BeastCraft", 339 | "decimals": 0, 340 | "editable": true, 341 | "error": false, 342 | "format": "percent", 343 | "gauge": { 344 | "maxValue": 100, 345 | "minValue": 0, 346 | "show": false, 347 | "thresholdLabels": false, 348 | "thresholdMarkers": true 349 | }, 350 | "height": "100px", 351 | "id": 9, 352 | "interval": null, 353 | "isNew": true, 354 | "links": [], 355 | "maxDataPoints": 100, 356 | "nullPointMode": "connected", 357 | "nullText": null, 358 | "postfix": "", 359 | "postfixFontSize": "50%", 360 | "prefix": "", 361 | "prefixFontSize": "50%", 362 | "span": 2, 363 | "sparkline": { 364 | "fillColor": "rgba(31, 118, 189, 0.18)", 365 | "full": true, 366 | "lineColor": "rgb(31, 120, 193)", 367 | "show": true 368 | }, 369 | "targets": [ 370 | { 371 | "dsType": "influxdb", 372 | "groupBy": [], 373 | "measurement": "ups.load", 374 | "policy": "default", 375 | "query": "SELECT \"value\" FROM \"ups.load\" WHERE $timeFilter", 376 | "rawQuery": true, 377 | "refId": "A", 378 | "resultFormat": "time_series", 379 | "select": [ 380 | [ 381 | { 382 | "params": [ 383 | "value" 384 | ], 385 | "type": "field" 386 | } 387 | ] 388 | ], 389 | "tags": [] 390 | } 391 | ], 392 | "thresholds": "", 393 | "title": "ups.load", 394 | "transparent": true, 395 | "type": "singlestat", 396 | "valueFontSize": "50%", 397 | "valueMaps": [ 398 | { 399 | "op": "=", 400 | "text": "N/A", 401 | "value": "null" 402 | } 403 | ], 404 | "valueName": "current" 405 | } 406 | ], 407 | "title": "Singlestats 1/2" 408 | }, 409 | { 410 | "collapse": false, 411 | "editable": true, 412 | "height": "0", 413 | "panels": [ 414 | { 415 | "cacheTimeout": null, 416 | "colorBackground": false, 417 | "colorValue": true, 418 | "colors": [ 419 | "rgba(50, 172, 45, 0.97)", 420 | "rgba(237, 129, 40, 0.89)", 421 | "rgba(245, 54, 54, 0.9)" 422 | ], 423 | "datasource": "BeastCraft", 424 | "decimals": 2, 425 | "editable": true, 426 | "error": false, 427 | "format": "volt", 428 | "gauge": { 429 | "maxValue": 100, 430 | "minValue": 0, 431 | "show": false, 432 | "thresholdLabels": false, 433 | "thresholdMarkers": true 434 | }, 435 | "height": "100px", 436 | "id": 5, 437 | "interval": null, 438 | "isNew": true, 439 | "links": [], 440 | "maxDataPoints": 100, 441 | "nullPointMode": "connected", 442 | "nullText": null, 443 | "postfix": "", 444 | "postfixFontSize": "50%", 445 | "prefix": "", 446 | "prefixFontSize": "50%", 447 | "span": 3, 448 | "sparkline": { 449 | "fillColor": "rgba(31, 118, 189, 0.18)", 450 | "full": true, 451 | "lineColor": "rgb(31, 120, 193)", 452 | "show": true 453 | }, 454 | "targets": [ 455 | { 456 | "dsType": "influxdb", 457 | "groupBy": [], 458 | "measurement": "input.voltage", 459 | "policy": "default", 460 | "query": "SELECT \"value\" FROM \"input.voltage\" WHERE $timeFilter", 461 | "rawQuery": true, 462 | "refId": "A", 463 | "resultFormat": "time_series", 464 | "select": [ 465 | [ 466 | { 467 | "params": [ 468 | "value" 469 | ], 470 | "type": "field" 471 | } 472 | ] 473 | ], 474 | "tags": [] 475 | } 476 | ], 477 | "thresholds": "240,245,250", 478 | "title": "input.voltage", 479 | "transparent": true, 480 | "type": "singlestat", 481 | "valueFontSize": "50%", 482 | "valueMaps": [ 483 | { 484 | "op": "=", 485 | "text": "N/A", 486 | "value": "null" 487 | } 488 | ], 489 | "valueName": "current" 490 | }, 491 | { 492 | "cacheTimeout": null, 493 | "colorBackground": false, 494 | "colorValue": true, 495 | "colors": [ 496 | "rgba(50, 172, 45, 0.97)", 497 | "rgba(237, 129, 40, 0.89)", 498 | "rgba(245, 54, 54, 0.9)" 499 | ], 500 | "datasource": "BeastCraft", 501 | "decimals": 2, 502 | "editable": true, 503 | "error": false, 504 | "format": "volt", 505 | "gauge": { 506 | "maxValue": 100, 507 | "minValue": 0, 508 | "show": false, 509 | "thresholdLabels": false, 510 | "thresholdMarkers": true 511 | }, 512 | "height": "100px", 513 | "id": 6, 514 | "interval": null, 515 | "isNew": true, 516 | "links": [], 517 | "maxDataPoints": 100, 518 | "nullPointMode": "connected", 519 | "nullText": null, 520 | "postfix": "", 521 | "postfixFontSize": "50%", 522 | "prefix": "", 523 | "prefixFontSize": "50%", 524 | "span": 3, 525 | "sparkline": { 526 | "fillColor": "rgba(31, 118, 189, 0.18)", 527 | "full": true, 528 | "lineColor": "rgb(31, 120, 193)", 529 | "show": true 530 | }, 531 | "targets": [ 532 | { 533 | "dsType": "influxdb", 534 | "groupBy": [], 535 | "measurement": "output.voltage", 536 | "policy": "default", 537 | "query": "SELECT \"value\" FROM \"output.voltage\" WHERE $timeFilter", 538 | "rawQuery": true, 539 | "refId": "A", 540 | "resultFormat": "time_series", 541 | "select": [ 542 | [ 543 | { 544 | "params": [ 545 | "value" 546 | ], 547 | "type": "field" 548 | } 549 | ] 550 | ], 551 | "tags": [] 552 | } 553 | ], 554 | "thresholds": "240,245,250", 555 | "title": "output.voltage", 556 | "transparent": true, 557 | "type": "singlestat", 558 | "valueFontSize": "50%", 559 | "valueMaps": [ 560 | { 561 | "op": "=", 562 | "text": "N/A", 563 | "value": "null" 564 | } 565 | ], 566 | "valueName": "current" 567 | }, 568 | { 569 | "cacheTimeout": null, 570 | "colorBackground": false, 571 | "colorValue": true, 572 | "colors": [ 573 | "rgba(50, 172, 45, 0.97)", 574 | "rgba(237, 129, 40, 0.89)", 575 | "rgba(245, 54, 54, 0.9)" 576 | ], 577 | "datasource": "BeastCraft", 578 | "decimals": 2, 579 | "editable": true, 580 | "error": false, 581 | "format": "hertz", 582 | "gauge": { 583 | "maxValue": 100, 584 | "minValue": 0, 585 | "show": false, 586 | "thresholdLabels": false, 587 | "thresholdMarkers": true 588 | }, 589 | "height": "100px", 590 | "id": 4, 591 | "interval": null, 592 | "isNew": true, 593 | "links": [], 594 | "maxDataPoints": 100, 595 | "nullPointMode": "connected", 596 | "nullText": null, 597 | "postfix": "", 598 | "postfixFontSize": "50%", 599 | "prefix": "", 600 | "prefixFontSize": "50%", 601 | "span": 3, 602 | "sparkline": { 603 | "fillColor": "rgba(31, 118, 189, 0.18)", 604 | "full": true, 605 | "lineColor": "rgb(31, 120, 193)", 606 | "show": true 607 | }, 608 | "targets": [ 609 | { 610 | "dsType": "influxdb", 611 | "groupBy": [], 612 | "measurement": "input.frequency", 613 | "policy": "default", 614 | "query": "SELECT \"value\" FROM \"input.frequency\" WHERE $timeFilter", 615 | "rawQuery": true, 616 | "refId": "A", 617 | "resultFormat": "time_series", 618 | "select": [ 619 | [ 620 | { 621 | "params": [ 622 | "value" 623 | ], 624 | "type": "field" 625 | } 626 | ] 627 | ], 628 | "tags": [] 629 | } 630 | ], 631 | "thresholds": "50.8,50.9", 632 | "title": "input.frequency", 633 | "transparent": true, 634 | "type": "singlestat", 635 | "valueFontSize": "50%", 636 | "valueMaps": [ 637 | { 638 | "op": "=", 639 | "text": "N/A", 640 | "value": "null" 641 | } 642 | ], 643 | "valueName": "current" 644 | }, 645 | { 646 | "cacheTimeout": null, 647 | "colorBackground": false, 648 | "colorValue": true, 649 | "colors": [ 650 | "rgba(50, 172, 45, 0.97)", 651 | "rgba(237, 129, 40, 0.89)", 652 | "rgba(245, 54, 54, 0.9)" 653 | ], 654 | "datasource": "BeastCraft", 655 | "decimals": 2, 656 | "editable": true, 657 | "error": false, 658 | "format": "celsius", 659 | "gauge": { 660 | "maxValue": 100, 661 | "minValue": 0, 662 | "show": false, 663 | "thresholdLabels": false, 664 | "thresholdMarkers": true 665 | }, 666 | "height": "100px", 667 | "id": 7, 668 | "interval": null, 669 | "isNew": true, 670 | "links": [], 671 | "maxDataPoints": 100, 672 | "nullPointMode": "connected", 673 | "nullText": null, 674 | "postfix": "", 675 | "postfixFontSize": "50%", 676 | "prefix": "", 677 | "prefixFontSize": "50%", 678 | "span": 3, 679 | "sparkline": { 680 | "fillColor": "rgba(31, 118, 189, 0.18)", 681 | "full": true, 682 | "lineColor": "rgb(31, 120, 193)", 683 | "show": true 684 | }, 685 | "targets": [ 686 | { 687 | "dsType": "influxdb", 688 | "groupBy": [], 689 | "measurement": "ups.temperature", 690 | "policy": "default", 691 | "query": "SELECT \"value\" FROM \"ups.temperature\" WHERE $timeFilter", 692 | "rawQuery": true, 693 | "refId": "A", 694 | "resultFormat": "time_series", 695 | "select": [ 696 | [ 697 | { 698 | "params": [ 699 | "value" 700 | ], 701 | "type": "field" 702 | } 703 | ] 704 | ], 705 | "tags": [] 706 | } 707 | ], 708 | "thresholds": "30,45", 709 | "title": "ups.temperature", 710 | "transparent": true, 711 | "type": "singlestat", 712 | "valueFontSize": "50%", 713 | "valueMaps": [ 714 | { 715 | "op": "=", 716 | "text": "N/A", 717 | "value": "null" 718 | } 719 | ], 720 | "valueName": "current" 721 | } 722 | ], 723 | "title": "Singelstats 2/2" 724 | }, 725 | { 726 | "collapse": false, 727 | "editable": true, 728 | "height": "250px", 729 | "panels": [ 730 | { 731 | "aliasColors": {}, 732 | "bars": false, 733 | "datasource": "BeastCraft", 734 | "decimals": 2, 735 | "editable": true, 736 | "error": false, 737 | "fill": 1, 738 | "grid": { 739 | "threshold1": null, 740 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 741 | "threshold2": null, 742 | "threshold2Color": "rgba(234, 112, 112, 0.22)", 743 | "thresholdLine": false 744 | }, 745 | "id": 10, 746 | "isNew": true, 747 | "legend": { 748 | "avg": false, 749 | "current": false, 750 | "max": false, 751 | "min": false, 752 | "rightSide": false, 753 | "show": true, 754 | "total": false, 755 | "values": false 756 | }, 757 | "lines": true, 758 | "linewidth": 2, 759 | "links": [], 760 | "nullPointMode": "connected", 761 | "percentage": false, 762 | "pointradius": 5, 763 | "points": false, 764 | "renderer": "flot", 765 | "seriesOverrides": [], 766 | "span": 4, 767 | "stack": false, 768 | "steppedLine": false, 769 | "targets": [ 770 | { 771 | "alias": "", 772 | "dsType": "influxdb", 773 | "groupBy": [ 774 | { 775 | "params": [ 776 | "$interval" 777 | ], 778 | "type": "time" 779 | }, 780 | { 781 | "params": [ 782 | "previous" 783 | ], 784 | "type": "fill" 785 | } 786 | ], 787 | "measurement": "battery.voltage", 788 | "policy": "default", 789 | "query": "SELECT mean(\"value\") FROM \"battery.voltage\" WHERE $timeFilter GROUP BY time($interval) fill(previous)", 790 | "refId": "A", 791 | "resultFormat": "time_series", 792 | "select": [ 793 | [ 794 | { 795 | "params": [ 796 | "value" 797 | ], 798 | "type": "field" 799 | }, 800 | { 801 | "params": [], 802 | "type": "mean" 803 | } 804 | ] 805 | ], 806 | "tags": [] 807 | } 808 | ], 809 | "timeFrom": null, 810 | "timeShift": null, 811 | "title": "battery.voltage", 812 | "tooltip": { 813 | "msResolution": false, 814 | "shared": true, 815 | "value_type": "individual" 816 | }, 817 | "transparent": true, 818 | "type": "graph", 819 | "xaxis": { 820 | "show": true 821 | }, 822 | "yaxes": [ 823 | { 824 | "format": "volt", 825 | "label": "", 826 | "logBase": 1, 827 | "max": null, 828 | "min": null, 829 | "show": true 830 | }, 831 | { 832 | "format": "none", 833 | "label": "", 834 | "logBase": 1, 835 | "max": null, 836 | "min": null, 837 | "show": true 838 | } 839 | ] 840 | }, 841 | { 842 | "aliasColors": {}, 843 | "bars": false, 844 | "datasource": "BeastCraft", 845 | "decimals": null, 846 | "editable": true, 847 | "error": false, 848 | "fill": 1, 849 | "grid": { 850 | "threshold1": null, 851 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 852 | "threshold2": null, 853 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 854 | }, 855 | "id": 11, 856 | "isNew": true, 857 | "legend": { 858 | "avg": false, 859 | "current": false, 860 | "max": false, 861 | "min": false, 862 | "show": true, 863 | "total": false, 864 | "values": false 865 | }, 866 | "lines": true, 867 | "linewidth": 2, 868 | "links": [], 869 | "nullPointMode": "connected", 870 | "percentage": false, 871 | "pointradius": 5, 872 | "points": false, 873 | "renderer": "flot", 874 | "seriesOverrides": [], 875 | "span": 4, 876 | "stack": false, 877 | "steppedLine": false, 878 | "targets": [ 879 | { 880 | "dsType": "influxdb", 881 | "groupBy": [ 882 | { 883 | "params": [ 884 | "$interval" 885 | ], 886 | "type": "time" 887 | }, 888 | { 889 | "params": [ 890 | "previous" 891 | ], 892 | "type": "fill" 893 | } 894 | ], 895 | "measurement": "ups.load", 896 | "policy": "default", 897 | "query": "SELECT mean(\"value\") FROM \"ups.load\" WHERE $timeFilter GROUP BY time($interval) fill(previous)", 898 | "refId": "A", 899 | "resultFormat": "time_series", 900 | "select": [ 901 | [ 902 | { 903 | "params": [ 904 | "value" 905 | ], 906 | "type": "field" 907 | }, 908 | { 909 | "params": [], 910 | "type": "mean" 911 | } 912 | ] 913 | ], 914 | "tags": [] 915 | } 916 | ], 917 | "timeFrom": null, 918 | "timeShift": null, 919 | "title": "ups.load", 920 | "tooltip": { 921 | "msResolution": false, 922 | "shared": true, 923 | "value_type": "cumulative" 924 | }, 925 | "transparent": true, 926 | "type": "graph", 927 | "xaxis": { 928 | "show": true 929 | }, 930 | "yaxes": [ 931 | { 932 | "format": "percent", 933 | "logBase": 1, 934 | "max": 100, 935 | "min": 0, 936 | "show": true 937 | }, 938 | { 939 | "format": "short", 940 | "logBase": 1, 941 | "max": null, 942 | "min": null, 943 | "show": true 944 | } 945 | ] 946 | }, 947 | { 948 | "aliasColors": {}, 949 | "bars": false, 950 | "datasource": "BeastCraft", 951 | "editable": true, 952 | "error": false, 953 | "fill": 1, 954 | "grid": { 955 | "threshold1": null, 956 | "threshold1Color": "rgba(216, 200, 27, 0.27)", 957 | "threshold2": null, 958 | "threshold2Color": "rgba(234, 112, 112, 0.22)" 959 | }, 960 | "id": 12, 961 | "isNew": true, 962 | "legend": { 963 | "avg": false, 964 | "current": false, 965 | "max": false, 966 | "min": false, 967 | "show": true, 968 | "total": false, 969 | "values": false 970 | }, 971 | "lines": true, 972 | "linewidth": 2, 973 | "links": [], 974 | "nullPointMode": "connected", 975 | "percentage": false, 976 | "pointradius": 5, 977 | "points": false, 978 | "renderer": "flot", 979 | "seriesOverrides": [], 980 | "span": 4, 981 | "stack": false, 982 | "steppedLine": false, 983 | "targets": [ 984 | { 985 | "dsType": "influxdb", 986 | "groupBy": [ 987 | { 988 | "params": [ 989 | "$interval" 990 | ], 991 | "type": "time" 992 | }, 993 | { 994 | "params": [ 995 | "previous" 996 | ], 997 | "type": "fill" 998 | } 999 | ], 1000 | "measurement": "battery.charge", 1001 | "policy": "default", 1002 | "query": "SELECT mean(\"value\") FROM \"battery.charge\" WHERE $timeFilter GROUP BY time($interval) fill(previous)", 1003 | "refId": "A", 1004 | "resultFormat": "time_series", 1005 | "select": [ 1006 | [ 1007 | { 1008 | "params": [ 1009 | "value" 1010 | ], 1011 | "type": "field" 1012 | }, 1013 | { 1014 | "params": [], 1015 | "type": "mean" 1016 | } 1017 | ] 1018 | ], 1019 | "tags": [] 1020 | } 1021 | ], 1022 | "timeFrom": null, 1023 | "timeShift": null, 1024 | "title": "battery.charge", 1025 | "tooltip": { 1026 | "msResolution": false, 1027 | "shared": true, 1028 | "value_type": "cumulative" 1029 | }, 1030 | "transparent": true, 1031 | "type": "graph", 1032 | "xaxis": { 1033 | "show": true 1034 | }, 1035 | "yaxes": [ 1036 | { 1037 | "format": "percent", 1038 | "logBase": 1, 1039 | "max": 100, 1040 | "min": 0, 1041 | "show": true 1042 | }, 1043 | { 1044 | "format": "short", 1045 | "logBase": 1, 1046 | "max": null, 1047 | "min": null, 1048 | "show": true 1049 | } 1050 | ] 1051 | } 1052 | ], 1053 | "title": "Graphs" 1054 | } 1055 | ], 1056 | "time": { 1057 | "from": "now-1h", 1058 | "to": "now" 1059 | }, 1060 | "timepicker": { 1061 | "now": true, 1062 | "refresh_intervals": [ 1063 | "5s", 1064 | "10s", 1065 | "30s", 1066 | "1m", 1067 | "5m", 1068 | "15m", 1069 | "30m", 1070 | "1h", 1071 | "2h", 1072 | "1d" 1073 | ], 1074 | "time_options": [ 1075 | "5m", 1076 | "15m", 1077 | "1h", 1078 | "6h", 1079 | "12h", 1080 | "24h", 1081 | "2d", 1082 | "7d", 1083 | "30d" 1084 | ] 1085 | }, 1086 | "templating": { 1087 | "list": [] 1088 | }, 1089 | "annotations": { 1090 | "list": [] 1091 | }, 1092 | "refresh": "1m", 1093 | "schemaVersion": 12, 1094 | "version": 8, 1095 | "links": [] 1096 | } 1097 | --------------------------------------------------------------------------------