├── requirements.txt ├── screenshots ├── driving_dash.png ├── charging_dash.png ├── projected_range.png ├── Component_Diagram.png ├── teslametrics_v2_1.png ├── teslametrics_v2_2.png ├── teslametrics_v2_3.png └── dashboardsetting_templatevar.png ├── grafana-datasources └── influxdb.json ├── dashboard2docker.sh ├── .gitignore ├── apiconfig.py ├── provisioning ├── dashboards │ └── all.yml └── datasources │ └── automatic.yml ├── tesla-apiscraper.service ├── Dockerfile.compose ├── srtmread.py ├── docker-compose.yml ├── config.py.compose ├── config.py.dist ├── Dockerfile ├── teslajson.py ├── LICENSE ├── README.md ├── apiscraper.py └── grafana-dashboards ├── Climate.json ├── Charging.json └── Driving.json /requirements.txt: -------------------------------------------------------------------------------- 1 | influxdb 2 | srtm.py -------------------------------------------------------------------------------- /screenshots/driving_dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/driving_dash.png -------------------------------------------------------------------------------- /screenshots/charging_dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/charging_dash.png -------------------------------------------------------------------------------- /screenshots/projected_range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/projected_range.png -------------------------------------------------------------------------------- /screenshots/Component_Diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/Component_Diagram.png -------------------------------------------------------------------------------- /screenshots/teslametrics_v2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/teslametrics_v2_1.png -------------------------------------------------------------------------------- /screenshots/teslametrics_v2_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/teslametrics_v2_2.png -------------------------------------------------------------------------------- /screenshots/teslametrics_v2_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/teslametrics_v2_3.png -------------------------------------------------------------------------------- /screenshots/dashboardsetting_templatevar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lephisto/tesla-apiscraper/HEAD/screenshots/dashboardsetting_templatevar.png -------------------------------------------------------------------------------- /grafana-datasources/influxdb.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"InfluxDB", 3 | "type":"influxdb", 4 | "url":"http://localhost:8086", 5 | "access":"proxy", 6 | "database":"tesla" 7 | } 8 | -------------------------------------------------------------------------------- /dashboard2docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cp grafana-dashboards/TeslaMetricsV2.json provisioning/dashboards/TeslaMetricsV2.json 3 | sed -i 's/${DS_TESLA}/Tesla/g' provisioning/dashboards/*.json 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude 2 | config.py 3 | teslajson-master/ 4 | master.zip 5 | *.pyc 6 | apiscraper.log 7 | .idea 8 | hgt/*.zip 9 | hgt/*.hgt 10 | hgt/*.downloading 11 | *.iml 12 | provisioning/dashboards/*.json 13 | -------------------------------------------------------------------------------- /apiconfig.py: -------------------------------------------------------------------------------- 1 | a_api = '/api/1/' 2 | a_id = 'e4a9949fcfa04068f59abb5a658f2bac0a3428e4652315490b659d5ab3f35a9e' 3 | a_secret = 'c75f14bbadc8bee3a7594412c31416f8300256d7668ea7e6e7f06727bfb9d220' 4 | a_baseurl = 'https://owner-api.teslamotors.com' 5 | -------------------------------------------------------------------------------- /provisioning/dashboards/all.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Tesla' 5 | orgId: 1 6 | folder: '' 7 | type: file 8 | disableDeletion: false 9 | editable: true 10 | options: 11 | path: /etc/grafana/provisioning/dashboards 12 | -------------------------------------------------------------------------------- /provisioning/datasources/automatic.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | deleteDatasources: 4 | - name: Tesla 5 | orgId: 1 6 | 7 | datasources: 8 | - name: Tesla 9 | type: influxdb 10 | access: proxy 11 | url: http://influxdb_apiscraper:8086 12 | password: tesla 13 | user: tesla 14 | database: tesla 15 | basicAuth: false 16 | basicAuthUser: 17 | basicAuthPassword: 18 | withCredentials: 19 | isDefault: true 20 | jsonData: 21 | tlsAuth: false 22 | tlsAuthWithCACert: false 23 | secureJsonData: 24 | tlsCACert: "" 25 | tlsClientCert: "" 26 | tlsClientKey: "" 27 | version: 1 28 | editable: true 29 | -------------------------------------------------------------------------------- /tesla-apiscraper.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Tesla API Scraper 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | Restart=always 8 | Users=pi 9 | WorkingDirectory=/home/pi/tesla-apiscraper 10 | 11 | # Remove old containers, images and volumes 12 | ExecStartPre=/usr/bin/docker-compose -f docker-compose.yml down -v 13 | ExecStartPre=/usr/bin/docker-compose -f docker-compose.yml rm -v 14 | ExecStartPre=-/bin/bash -c 'docker volume rm $(docker volume ls -q)' 15 | ExecStartPre=-/bin/bash -c 'docker rmi $(docker images | grep "" | awk \'{print $3}\')' 16 | ExecStartPre=-/bin/bash -c 'docker rm -v $(docker ps -aq)' 17 | 18 | # Compose up 19 | ExecStart=/usr/bin/docker-compose -f docker-compose.yml up --no-color 20 | 21 | # Compose down, remove containers and volumes 22 | ExecStop=/usr/bin/docker-compose -f docker-compose.yml down -v --no-color 23 | 24 | [Install] 25 | WantedBy=multi-user.target 26 | -------------------------------------------------------------------------------- /Dockerfile.compose: -------------------------------------------------------------------------------- 1 | # Dockerfile for use with docker-compose 2 | 3 | FROM debian:stretch-slim 4 | 5 | RUN apt-get -y update 6 | 7 | # Install Python 8 | RUN apt-get -y install python3 9 | RUN apt-get -y install apt-transport-https 10 | RUN apt-get -y install curl 11 | RUN apt-get -y install gnupg2 12 | RUN apt-get -y install git 13 | 14 | # Install Tesla API Scraper 15 | RUN apt-get -y install python3-pip 16 | WORKDIR / 17 | RUN pip3 install influxdb 18 | 19 | RUN git clone https://github.com/tkrajina/srtm.py 20 | WORKDIR srtm.py 21 | RUN python3 ./setup.py install 22 | WORKDIR / 23 | 24 | ARG CACHEBUST=1 25 | ARG gitversion 26 | # Configure it 27 | RUN git clone https://github.com/lephisto/tesla-apiscraper 28 | WORKDIR tesla-apiscraper 29 | RUN git pull 30 | RUN git checkout $gitversion 31 | # Define our startup script 32 | RUN echo "#!/bin/bash" > /start.sh 33 | RUN echo "python3 /tesla-apiscraper/apiscraper.py" >> /start.sh 34 | RUN chmod +x /start.sh 35 | 36 | # Run it 37 | EXPOSE 8023 38 | CMD /start.sh 39 | -------------------------------------------------------------------------------- /srtmread.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from pathlib import Path 4 | 5 | import srtm 6 | 7 | 8 | 9 | def elevationtoinflux(lat, lon, vin, displayname, ts, ifclient, dryrun, logger): 10 | if not os.path.isfile('srtm.lck.' + str(os.getpid())): 11 | Path('srtm.lck.' + str(os.getpid())).touch() 12 | elevation_data = srtm.get_data() 13 | elevation = elevation_data.get_elevation(lat, lon) 14 | os.remove('srtm.lck.' + str(os.getpid())) 15 | logger.debug("Elevation: " + str(elevation)) 16 | elev_json_body = [ 17 | { 18 | "measurement": "drive_state", 19 | "tags": { 20 | "vin": vin, 21 | "display_name": displayname, 22 | }, 23 | "time": int(ts * 1000000), 24 | "fields": { 25 | "elevation": elevation 26 | } 27 | } 28 | ] 29 | 30 | if not dryrun and elevation is not None: 31 | ifclient.write_points(elev_json_body) 32 | else: 33 | print("Lockfile detected, skipping") 34 | sys.exit() 35 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | 4 | services: 5 | 6 | influxdb: 7 | image: "influxdb:latest" 8 | container_name: "influxdb_apiscraper" 9 | environment: 10 | - INFLUXDB_DB="tesla" 11 | volumes: 12 | - /opt/apiscraper/influxdb:/var/lib/influxdb 13 | 14 | grafana: 15 | image: grafana/grafana:latest 16 | container_name: "grafana_apiscraper" 17 | environment: 18 | - GF_INSTALL_PLUGINS=natel-discrete-panel,https://mephis.to/grafana-trackmap-panel/dist.zip;grafana-trackmap-panel 19 | # - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana/ <<< Set this if you want to hide Grafana begind a subpath, eg: https://yourhomeseerver.url/grafana/ 20 | 21 | ports: 22 | - "3000:3000" 23 | links: 24 | - influxdb 25 | user: "472" 26 | volumes: 27 | - /opt/apiscraper/grafana:/var/lib/grafana 28 | - ./provisioning/:/etc/grafana/provisioning 29 | 30 | apiscraper: 31 | container_name: apiscraper 32 | build: 33 | args: 34 | gitversion: 69e2af3cec804688c01c7c1b85a130997ca6107b 35 | context: . 36 | dockerfile: Dockerfile.compose 37 | volumes: 38 | - ./config.py:/tesla-apiscraper/config.py 39 | - ./apiscraper.log:/tesla-apiscraper/apiscraper.log 40 | ports: 41 | - "8023:8023" 42 | restart: on-failure:5 43 | depends_on: 44 | - influxdb 45 | -------------------------------------------------------------------------------- /config.py.compose: -------------------------------------------------------------------------------- 1 | # Telsa Credentials 2 | a_tesla_email = '' # myTesla Account Username 3 | a_tesla_password = '' # myTesla Account Password 4 | a_tesla_car_idx = 0 # Index of the Car in myTesla Account in case of multiple Cars 5 | a_allow_sleep = 1 # Allow Car to fall asleep / don't wake up if car is asleep on Scraper start 6 | a_maximum_sleep = 1024 # Wake up every X seconds, if car is asleep 7 | 8 | # Scraper API Config (you need that for using the android App) 9 | a_enable_api = True 10 | a_api_key = 'somerandomnumberwithenoughdigitsthatcantbeguessedeasily' 11 | a_api_port = 8023 12 | a_start_disabled = False # Scrapper state on startup. This could 13 | # be changed with the app if the 14 | # a_enable_api is True 15 | a_resolve_elevation = True # Resolve Coordinates to Elevation. Will Autodownload Elevation data from https://dds.cr.usgs.gov 16 | 17 | # Logging 18 | a_logfile = 'apiscraper.log' # APIScraper Logfile 19 | #CRITICAL = 50 20 | #FATAL = CRITICAL 21 | #ERROR = 40 22 | #WARNING = 30 23 | #WARN = WARNING 24 | #INFO = 20 25 | #DEBUG = 10 26 | #NOTSET = 0 27 | a_loglevel = 20 # Loglevel 10: DE 28 | 29 | # Influx DB Credentials 30 | a_influx_host = 'influxdb_apiscraper' # Set this to influxdb_apiscraper in a Docker environment, otherwise: localhost 31 | a_influx_port = 8086 # InfluxDB Port 32 | a_influx_user = 'tesla' # InfluxDB Username 33 | a_influx_pass = '' # InfluxDB Password 34 | a_influx_db = 'tesla' # InfluxDB Datebasename 35 | 36 | a_dry_run = False # Just for debugging purposes 37 | -------------------------------------------------------------------------------- /config.py.dist: -------------------------------------------------------------------------------- 1 | # Telsa Credentials 2 | a_tesla_email = '' # myTesla Account Username 3 | a_tesla_password = '' # myTesla Account Password 4 | a_tesla_car_idx = 0 # Index of the Car in myTesla Account in case of multiple Cars 5 | a_allow_sleep = 1 # Allow Car to fall asleep / don't wake up if car is asleep on Scraper start 6 | a_maximum_sleep = 1024 # Wake up every X seconds, if car is asleep 7 | 8 | # Scraper API Config (you need that for using the android App) 9 | a_enable_api = True 10 | a_api_key = 'somerandomnumberwithenoughdigitsthatcantbeguessedeasily' 11 | a_api_port = 8023 12 | a_start_disabled = False # Scrapper state on startup. This could 13 | # be changed with the app if the 14 | # a_enable_api is True 15 | a_resolve_elevation = True # Resolve Coordinates to Elevation. Will Autodownload Elevation data from https://dds.cr.usgs.gov 16 | 17 | # Logging 18 | a_logfile = 'apiscraper.log' # APIScraper Logfile 19 | #CRITICAL = 50 20 | #FATAL = CRITICAL 21 | #ERROR = 40 22 | #WARNING = 30 23 | #WARN = WARNING 24 | #INFO = 20 25 | #DEBUG = 10 26 | #NOTSET = 0 27 | a_loglevel = 20 # Loglevel 10: DE 28 | 29 | # Influx DB Credentials 30 | a_influx_host = 'localhost' # Set this to influxdb_apiscraper in a Docker environment, otherwise: localhost 31 | a_influx_port = 8086 # InfluxDB Port 32 | a_influx_user = 'tesla' # InfluxDB Username 33 | a_influx_pass = '' # InfluxDB Password 34 | a_influx_db = 'tesla' # InfluxDB Datebasename 35 | 36 | a_dry_run = False # Just for debugging purposes 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Dockerfile for standalone usage 2 | FROM debian:stretch-slim 3 | 4 | RUN apt-get -y update 5 | 6 | # Install Python 7 | RUN apt-get -y install python3 8 | RUN apt-get -y install apt-transport-https 9 | RUN apt-get -y install curl 10 | RUN apt-get -y install gnupg2 11 | 12 | # Install Influx 13 | RUN curl -sL https://repos.influxdata.com/influxdb.key | apt-key add - 14 | RUN echo "deb https://repos.influxdata.com/debian stretch stable" | tee /etc/apt/sources.list.d/influxdb.list 15 | RUN apt-get -y update 16 | RUN apt-get -y install influxdb 17 | 18 | # Install Grafana 19 | RUN curl https://packages.grafana.com/gpg.key | apt-key add - 20 | RUN echo "deb https://packages.grafana.com/oss/deb stable main" | tee /etc/apt/sources.list.d/grafana.list 21 | RUN apt-get -y update 22 | RUN apt-get -y install grafana 23 | 24 | # Install Node 25 | RUN curl -sL https://deb.nodesource.com/setup_11.x | bash - 26 | RUN apt-get install -y nodejs 27 | 28 | # Install Grafana addons 29 | RUN apt-get -y install git 30 | WORKDIR /var/lib/grafana/plugins 31 | RUN git clone https://github.com/lephisto/grafana-trackmap-panel 32 | WORKDIR /var/lib/grafana/plugins/grafana-trackmap-panel 33 | RUN git checkout v2.0.4-teslascraper 34 | RUN npm install 35 | RUN npm audit fix 36 | RUN npm run build 37 | RUN grafana-cli plugins install natel-discrete-panel 38 | 39 | # Install Tesla API Scraper 40 | RUN apt-get -y install python3-pip 41 | WORKDIR / 42 | RUN git clone https://github.com/lephisto/tesla-apiscraper 43 | RUN pip3 install influxdb 44 | 45 | RUN git clone https://github.com/tkrajina/srtm.py 46 | WORKDIR srtm.py 47 | RUN python3 ./setup.py install 48 | WORKDIR / 49 | 50 | # Configure it 51 | WORKDIR tesla-apiscraper 52 | RUN git checkout v2019.5 53 | RUN cp config.py.dist config.py 54 | 55 | # Create temp files for dashboard API calls 56 | RUN echo '{"dashboard":' > /tmp/TeslaMetricsV2.json 57 | RUN cat ./grafana-dashboards/TeslaMetricsV2.json >> /tmp/TeslaMetricsV2.json 58 | RUN echo '}' >> /tmp/TeslaMetricsV2.json 59 | RUN sed -i 's/\${DS_TESLA}/InfluxDB/g' /tmp/TeslaMetricsV2.json 60 | 61 | # Install Grafana data source and dashboards 62 | RUN service grafana-server start ; sleep 5 && \ 63 | curl -v -H 'Content-Type: application/json' -d @./grafana-datasources/influxdb.json http://admin:admin@localhost:3000/api/datasources && \ 64 | curl -v -H 'Content-Type: application/json' -d @/tmp/TeslaMetricsV2.json http://admin:admin@localhost:3000/api/dashboards/db && \ 65 | service grafana-server stop 66 | 67 | RUN sed -i "s/a_influxpass = ''/a_influx_pass = None/g" /tesla-apiscraper/config.py 68 | RUN sed -i "s/a_influxuser = 'tesla'/a_influx_user = None/g" /tesla-apiscraper/config.py 69 | 70 | # Define our startup script 71 | RUN echo "#!/bin/bash" > /start.sh 72 | RUN echo "sed -i \"s//\${TESLA_USERNAME}/g\" /tesla-apiscraper/config.py" >> /start.sh 73 | RUN echo "sed -i \"s//\${TESLA_PASSWORD}/g\" /tesla-apiscraper/config.py" >> /start.sh 74 | RUN echo "service influxdb start" >> /start.sh 75 | RUN echo "influx -execute \"create database tesla\"" >>/start.sh 76 | RUN echo "service grafana-server start" >> /start.sh 77 | RUN echo "python3 /tesla-apiscraper/apiscraper.py" >> /start.sh 78 | RUN chmod +x /start.sh 79 | 80 | # Persist Influx Data 81 | VOLUME ["/var/lib/influxdb"] 82 | 83 | # Run it 84 | EXPOSE 3000 85 | EXPOSE 8023 86 | CMD /start.sh 87 | -------------------------------------------------------------------------------- /teslajson.py: -------------------------------------------------------------------------------- 1 | """ Simple Python class to access the Tesla JSON API 2 | https://github.com/gglockner/teslajson 3 | 4 | The Tesla JSON API is described at: 5 | http://docs.timdorr.apiary.io/ 6 | 7 | Example: 8 | 9 | import teslajson 10 | c = teslajson.Connection('youremail', 'yourpassword') 11 | v = c.vehicles[0] 12 | v.wake_up() 13 | v.data_request('charge_state') 14 | v.command('charge_start') 15 | 16 | 17 | Modified by mephisto to get rid of access to a thirdparty 18 | 19 | """ 20 | 21 | try: # Python 3 22 | from urllib.parse import urlencode 23 | from urllib.request import Request, build_opener 24 | from urllib.request import ProxyHandler, HTTPBasicAuthHandler, HTTPHandler 25 | except: # Python 2 26 | from urllib import urlencode 27 | from urllib2 import Request, build_opener 28 | from urllib2 import ProxyHandler, HTTPBasicAuthHandler, HTTPHandler 29 | import json 30 | import datetime 31 | import calendar 32 | 33 | from apiconfig import * 34 | 35 | class Connection(object): 36 | """Connection to Tesla Motors API""" 37 | def __init__(self, 38 | email='', 39 | password='', 40 | access_token='', 41 | proxy_url = '', 42 | proxy_user = '', 43 | proxy_password = ''): 44 | 45 | """Initialize connection object 46 | 47 | Sets the vehicles field, a list of Vehicle objects 48 | associated with your account 49 | 50 | Required parameters: 51 | email: your login for teslamotors.com 52 | password: your password for teslamotors.com 53 | 54 | Optional parameters: 55 | access_token: API access token 56 | proxy_url: URL for proxy server 57 | proxy_user: username for proxy server 58 | proxy_password: password for proxy server 59 | """ 60 | self.proxy_url = proxy_url 61 | self.proxy_user = proxy_user 62 | self.proxy_password = proxy_password 63 | current_client = a_api 64 | self.baseurl = a_baseurl 65 | self.api = a_api 66 | if access_token: 67 | self.__sethead(access_token) 68 | else: 69 | self.oauth = { 70 | "grant_type" : "password", 71 | "client_id" : a_id, 72 | "client_secret" : a_secret, 73 | "email" : email, 74 | "password" : password } 75 | self.expiration = 0 # force refresh 76 | self.vehicles = [Vehicle(v, self) for v in self.get('vehicles')['response']] 77 | 78 | def refresh_vehicle(self): 79 | self.vehicles = [Vehicle(v, self) for v in self.get('vehicles')['response']] 80 | 81 | def get(self, command): 82 | """Utility command to get data from API""" 83 | return self.post(command, None) 84 | 85 | def post(self, command, data={}): 86 | """Utility command to post data to API""" 87 | now = calendar.timegm(datetime.datetime.now().timetuple()) 88 | if now > self.expiration: 89 | auth = self.__open("/oauth/token", data=self.oauth) 90 | self.__sethead(auth['access_token'], 91 | auth['created_at'] + auth['expires_in'] - 86400) 92 | return self.__open("%s%s" % (self.api, command), headers=self.head, data=data) 93 | 94 | def __sethead(self, access_token, expiration=float('inf')): 95 | """Set HTTP header""" 96 | self.access_token = access_token 97 | self.expiration = expiration 98 | self.head = {"Authorization": "Bearer %s" % access_token} 99 | 100 | def __open(self, url, headers={}, data=None, baseurl=""): 101 | """Raw urlopen command""" 102 | if not baseurl: 103 | baseurl = self.baseurl 104 | req = Request("%s%s" % (baseurl, url), headers=headers) 105 | try: 106 | req.data = urlencode(data).encode('utf-8') # Python 3 107 | except: 108 | try: 109 | req.add_data(urlencode(data)) # Python 2 110 | except: 111 | pass 112 | 113 | # Proxy support 114 | if self.proxy_url: 115 | if self.proxy_user: 116 | proxy = ProxyHandler({'https': 'https://%s:%s@%s' % (self.proxy_user, 117 | self.proxy_password, 118 | self.proxy_url)}) 119 | auth = HTTPBasicAuthHandler() 120 | opener = build_opener(proxy, auth, HTTPHandler) 121 | else: 122 | handler = ProxyHandler({'https': self.proxy_url}) 123 | opener = build_opener(handler) 124 | else: 125 | opener = build_opener() 126 | resp = opener.open(req) 127 | charset = resp.info().get('charset', 'utf-8') 128 | return json.loads(resp.read().decode(charset)) 129 | 130 | 131 | class Vehicle(dict): 132 | """Vehicle class, subclassed from dictionary. 133 | 134 | There are 3 primary methods: wake_up, data_request and command. 135 | data_request and command both require a name to specify the data 136 | or command, respectively. These names can be found in the 137 | Tesla JSON API.""" 138 | def __init__(self, data, connection): 139 | """Initialize vehicle class 140 | 141 | Called automatically by the Connection class 142 | """ 143 | super(Vehicle, self).__init__(data) 144 | self.connection = connection 145 | 146 | def data_request(self, name): 147 | """Get vehicle data""" 148 | result = self.get('data_request/%s' % name) 149 | return result['response'] 150 | 151 | def wake_up(self): 152 | """Wake the vehicle""" 153 | return self.post('wake_up') 154 | 155 | def command(self, name, data={}): 156 | """Run the command for the vehicle""" 157 | return self.post('command/%s' % name, data) 158 | 159 | def get(self, command): 160 | """Utility command to get data from API""" 161 | return self.connection.get('vehicles/%i/%s' % (self['id'], command)) 162 | 163 | def post(self, command, data={}): 164 | """Utility command to post data to API""" 165 | return self.connection.post('vehicles/%i/%s' % (self['id'], command), data) 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tesla-apiscraper 2 | Selfhosted API Scraper for pulling Vehicle Telemetry from the Tesla Owner API into an InfluxDB visualisation on Grafana Dashboards. 3 | 4 | Known to work with Model S, X and 3. 5 | 6 | _Current Release: v2019.5_ 7 | 8 | **Putting an end to __handing out the Key__ for your 100+ Grand Car to a third party you don't know.** 9 | 10 | This can be hosted on any System that's capable of running InfluxDB, Grafana and Python. In this short guide I assume you're using a Debian'ish OS. It can run on a dedicated Linuxserver out there on the Internets or on your home Raspberry Pi. 11 | 12 | It also has it's own API for use with an Android app or your own custom implementation, this will make sure you can start/stop/resume scraping your car. The App for Android is available on [here on Google Play](https://play.google.com/store/apps/details?id=to.mephis.apiscrapercontrol) 13 | 14 | The current App Version is 1.2.8 15 | 16 | ## Features 17 | 18 | - Capable of handling multiple Vehicles in one Tesla Account 19 | - Extended Sleep support: Car will fall asleep after certain time of no charging and no driving. Monitoring will continue withing 60 Seconds on car usage. 20 | - Control, comes with built-in API for use with an Android app or custom implementation to stop/resume scraping 21 | 22 | 23 | ## Screenshots 24 | 25 | ![Driving Dashboard](https://raw.githubusercontent.com/lephisto/tesla-apiscraper/master/screenshots/teslametrics_v2_1.png) 26 | 27 | ![Charging Dashboard](https://raw.githubusercontent.com/lephisto/tesla-apiscraper/master/screenshots/teslametrics_v2_2.png) 28 | 29 | Projected 100% Range: 30 | 31 | ![Projected Graph](https://raw.githubusercontent.com/lephisto/tesla-apiscraper/master/screenshots/teslametrics_v2_3.png) 32 | 33 | ## Installation: 34 | 35 | We updated to Python3 since Python2 is being phased out soon. Python2 is unsupported as of now. 36 | 37 | - Install Python3 38 | 39 | 40 | eg: 41 | ``` 42 | sudo apt install python3 python3-pathlib python3-pip python3-influxdb 43 | ``` 44 | 45 | - Install InfluxDB as in https://docs.influxdata.com/influxdb/v1.7/introduction/installation/ and create a Database where you want to store your Data in: 46 | 47 | ``` 48 | user@horst:~$ influx 49 | Connected to http://localhost:8086 version 1.7.2 50 | InfluxDB shell version: 1.7.2 51 | Enter an InfluxQL query 52 | > create database tesla 53 | ``` 54 | 55 | Additionally I suggest you to setup authentication or close the InfluxDB Port with a Packetfileter of your choice, if the Machine you use for Scraping has a Internetfacing Interface. 56 | 57 | - Install Grafana as in http://docs.grafana.org/installation/debian/ 58 | 59 | - Get Grafana grafana-trackmap-panel (and required node package manager) 60 | 61 | ``` 62 | apt install npm 63 | cd /var/lib/grafana/plugins 64 | git clone https://github.com/lephisto/grafana-trackmap-panel 65 | cd grafana-trackmap-panel 66 | git checkout v2.0.4-teslascraper 67 | npm install 68 | npm run build 69 | ``` 70 | 71 | - Get Grafana natel-discrete-panel 72 | 73 | ``` 74 | grafana-cli plugins install natel-discrete-panel 75 | ``` 76 | - Restart grafana-server afterwards 77 | 78 | ``` 79 | systemctl restart grafana-server.service 80 | ``` 81 | 82 | - Import the Dashboard JSON Files included in this repository. 83 | 84 | **Note to the US-Users**: Since the API reports all Range Values in Miles, i included two dashboard variables to match your preferences. By default the Conversion to km / kph is done, to get rid of this go to the dashboard settings: 85 | 86 | ![Driving Dashboard](https://raw.githubusercontent.com/lephisto/tesla-apiscraper/master/screenshots/dashboardsetting_templatevar.png) 87 | 88 | There you can change $rangeunit "km" to "mi" and $rangefactor 1.60934 to 1.0 and you're good to go. 89 | 90 | ## Install API Scraper 91 | 92 | - Get API Scraper 93 | 94 | ``` 95 | git clone https://github.com/lephisto/tesla-apiscraper 96 | ``` 97 | 98 | - Install requirements for Elevation Calculation 99 | 100 | ``` 101 | git clone https://github.com/tkrajina/srtm.py 102 | cd srtm.py 103 | python3 ./setup.py install --user 104 | cd .. 105 | ``` 106 | 107 | Important: 108 | ``` 109 | rm -rf srtm.py 110 | ``` 111 | 112 | 113 | - Always pick a release 114 | 115 | eg: 116 | ``` 117 | cd tesla-apiscraper 118 | git checkout v2019.5 119 | ``` 120 | 121 | - Configure API Scraper 122 | 123 | ``` 124 | cd tesla-apiscraper 125 | cp config.py.dist config.py 126 | nano config.py 127 | ``` 128 | 129 | Set Tesla and Influxdb Credentials there. 130 | 131 | 132 | Afterwards start the Scraping: 133 | 134 | ``` 135 | python3 ./apiscraper.py 136 | ``` 137 | 138 | Once you know everything is running fine you can start the scraper to keep running with screen or tmux, or feel free to write down a systemd service definition file. 139 | 140 | ``` 141 | tmux new-session -s apiscraper 'python3 apiscraper.py' 142 | ``` 143 | 144 | ## Building with Docker 145 | 146 | Alternatively, you can build and run tesla-apiscraper via Docker. There are two methods to run Docker stuff: Standalone and docker-compose. I recommend docker-compose in terms of the ability to update components properly. 147 | 148 | ### Standalone (deprecated): 149 | 150 | ``` 151 | mkdir -p /opt/apiscraper/influxdb 152 | docker build ./ -t tesla-apiscraper 153 | ``` 154 | 155 | Run: 156 | 157 | ``` 158 | docker run -p 3000:3000 -p 8023:8023 -v /opt/apiscraper/influxdb:/var/lib/influxdb -e "TESLA_USERNAME=" -e "TESLA_PASSWORD=" tesla-apiscraper:latest 159 | ``` 160 | 161 | In this case the timeseries data will persist in /opt/apiscraper/influxdb on your Dockerhost. Feel free to adjust to your needs. 162 | 163 | ### Docker-Compose 164 | 165 | Copy config 166 | 167 | ``` 168 | cp config.py.compose config.py 169 | ``` 170 | 171 | Edit your settings, at least put in your MyTesla credentials 172 | 173 | ``` 174 | nano config.py 175 | ``` 176 | 177 | The configuration is mapped outside the container, so you can conventiently change the Configuration the same way you would without running docker, restart the container and you're good to go. Same goes for the Logfile. 178 | 179 | Important: Create empty Log, otherwise bindmount will fail. 180 | 181 | ``` 182 | touch apiscraper.log 183 | ``` 184 | 185 | Create Directories for persistent Data: 186 | 187 | ``` 188 | sudo mkdir -p /opt/apiscraper/influxdb 189 | sudo mkdir -p /opt/apiscraper/grafana 190 | sudo chown 472 /opt/apiscraper/grafana 191 | ``` 192 | 193 | Start Docker Stack 194 | 195 | ``` 196 | ./dashboard2docker.sh 197 | docker-compose up 198 | ``` 199 | 200 | to keep the Console detached: 201 | 202 | ``` 203 | docker-compose up -d 204 | ``` 205 | 206 | to stop the Stack: 207 | 208 | ``` 209 | docker-compose down 210 | ``` 211 | 212 | to update and rebuild the whole Stack: 213 | to rebuild the whole Stack: 214 | 215 | ``` 216 | docker-compose pull 217 | docker-compose build --build-arg CACHEBUST=$(date +%s) apiscraper 218 | docker-compose up --force-recreate --build 219 | ``` 220 | 221 | You can now reach your Grafana Instance at http://localhost:3000 222 | 223 | #### A note on Docker on the Raspberry Pi: 224 | 225 | Raspian comes with a fairly outdated Version of Docker. Get a current one with: 226 | 227 | ``` 228 | curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh 229 | apt-get install docker-compose 230 | ``` 231 | 232 | Since you maybe want to handle Docker as non-root User, type: 233 | ``` 234 | usermod -aG docker pi 235 | reboot 236 | ``` 237 | ... adds pi or any other user you would like to the Docker Group. 238 | 239 | Make it start on boot: 240 | 241 | ``` 242 | cp tesla-apiscraper.service /lib/systemd/system 243 | sudo systemctl daemon-reload 244 | sudo systemctl enable tesla-apiscraper.service 245 | ``` 246 | 247 | 248 | ~~Note for Pi Zero Users: Since there's a glitch in 18.10.* on the ArmV6 you want to downgrade to docker-ce=18.06.1~ce~3-0~raspbian~~ 249 | 250 | This probably won't work on a Pi zero W, since ArmV6 is too weak. 251 | 252 | ## Using the API for the Scraper App for android 253 | 254 | There's a little Android App, that can help you letting your car sleep and immidiately turn on scraping when needed. You need to uncomment and configure the follwing Values for it in config.py: 255 | 256 | ``` 257 | a_enableapi = True 258 | a_apikey = 'somerandomnumberwithenoughdigitsthatcantbeguessedeasily' 259 | a_apiport = 8023 260 | ``` 261 | 262 | I strongly recommend to put all this behind a reverse Proxy, probably with HTTP Basic authentication in addition to the API Key. 263 | 264 | ## Using the API for the Scraper App for Android or custom implementation 265 | 266 | When calling from a custom implementation ensure you set the headers correctly and format your data as JSON. Examples in `curl` are included below 267 | 268 | ``` 269 | # Getting status: 270 | curl --header "Content-type: application/json" --header "apikey: somerandomnumberwithenoughdigitsthatcantbeguessedeasily" http://127.0.0.1:8023/state 271 | # Start/resume scrape: 272 | curl -X POST --header "Content-type: application/json" --header "apikey: somerandomnumberwithenoughdigitsthatcantbeguessedeasily" http://127.0.0.1:8023/switch --data '{"command":"scrape","value":""}' 273 | # Stop scrape: 274 | curl -X POST --header "Content-type: application/json" --header "apikey: somerandomnumberwithenoughdigitsthatcantbeguessedeasily" http://127.0.0.1:8023/switch --data '{"command":"scrape","value":"False"}' 275 | # Single call 276 | curl -X POST --header "Content-type: application/json" --header "apikey: somerandomnumberwithenoughdigitsthatcantbeguessedeasily" http://127.0.0.1:8023/switch --data '{"command":"oneshot","value":""}' 277 | An exmple Apache Reverseproxy configuration would look like: 278 | 279 | ``` 280 | #Apiscraper 281 | ProxyPass /scraperapi http://localhost:8023 282 | ProxyPassReverse /scraperapi http://localhost:8023 283 | #Grafana 284 | ProxyPass /grafana http://localhost:3000 285 | ProxyPassReverse /grafana http://localhost:3000 286 | ``` 287 | 288 | ## Known Limitations and issues 289 | 290 | - If you narrow down Timefilter too much, and there are no Measurements, you won't see anything in the Graph and Discrete. 291 | - The Code is far from being clean and in some way spaghetti'ish. This will be cleaned up in future Versions. 292 | - Boolean Values from the API currently won't show 293 | 294 | ## Some remarks about the Owner APIScraper 295 | 296 | - As stated below, the owner API is crafted for being used by the ios or android app. One challenge was to implement a reliable sleepmode. If the car is awake, the API keeps it awake, as long as requests occur. Once it falls asleep, parts of the API can be called to check the sleep state without waking up the car, however, when the scraper detects that the car doesn't change Values (not driving, not charging), it increases the Poll interval until the Car falls asleep. Once it's asleep it checks the sleepstate every Minute. This ensures, that the stats don't miss relevant portions of rides when the car was just woken up, an issue some other monitoring implementations suffer from. 297 | 298 | ## No 299 | 300 | - Due to incoming inquiries: I won't host an Instance of this for you nor provide any extensive Setup Support. This is aimed at people who know what they're doing. If there are Issues, open a Github Issue. 301 | 302 | ## More Disclaimer 303 | 304 | - Please note that the use of the Tesla REST API in general and the use of this software in particular is not endorsed by Tesla. You use this software at your own risk. The author does not take responsibility for anything related to the use of this software. 305 | 306 | ## Roadmap 307 | 308 | - Multithreaded statpulling 309 | - Code Cleanup (feel free to send PR :) 310 | - Move from influxql to it's successor flux, the upcoming query language for InfluxDB 311 | - Write some Tickscripts for alerting 312 | - Have a color gradient on geolocation that reflects any metric like speed for instance 313 | - Improve sleepinitiation 314 | 315 | ## Credits 316 | 317 | - Tesla API Interface forked from Greg Glockner https://github.com/gglockner/teslajson (removed pulling Tesla API Credentials from a pastebin whish seemed fishy..) 318 | - Things stolen from basic Script from cko from the german tff-forum.de 319 | -------------------------------------------------------------------------------- /apiscraper.py: -------------------------------------------------------------------------------- 1 | """ apiscraper.py - log all Tesla data available from the 2 | Mothership API to Influx. 3 | 4 | Author: Bastian Maeuser 5 | https://github.com/lephisto/tesla-apiscraper 6 | 7 | Please note that the use of the Tesla REST API in general 8 | and the use of this software in particular is not endorsed by Tesla. 9 | You use this software at your own risk. 10 | The author does not take responsibility for anything related 11 | to the use of this software. 12 | 13 | Thanks go to cko from the german tff-forum for a basic version of the 14 | script. 15 | 16 | Configuration is taken from config.py - a sample file is distributed as 17 | config.py.dist. Rename it and configure it according your needs. 18 | 19 | """ 20 | 21 | import json 22 | import logging 23 | import queue 24 | import sys 25 | import threading 26 | import time 27 | from http.server import HTTPServer, BaseHTTPRequestHandler 28 | from urllib.error import URLError 29 | 30 | from influxdb import InfluxDBClient 31 | from urllib3.exceptions import HTTPError 32 | 33 | import teslajson 34 | from config import * 35 | from srtmread import elevationtoinflux 36 | 37 | a_vin = "" 38 | a_display_name = "" 39 | a_ignore = ["media_state", "software_update", "speed_limit_mode"] 40 | 41 | a_validity_checks = { 42 | "charger_voltage": 43 | { 44 | "eval": "new_value < 10", 45 | "set": 0 46 | } 47 | } 48 | 49 | postq = queue.Queue() 50 | http_condition = threading.Condition() 51 | 52 | poll_interval = 1 # Set to -1 to wakeup the Car on Scraper start 53 | poll_count = 0 # Track pollcounting 54 | asleep_since = 0 55 | is_asleep = '' 56 | disableScrape = a_start_disabled 57 | disabled_since = 0 58 | busy_since = 0 59 | car_active_state = None 60 | last_data_from_tesla = None 61 | resume = False 62 | 63 | # DON'T CHANGE ANYTHING BELOW 64 | scraper_api_version = 2019.5 65 | 66 | influx_client = InfluxDBClient( 67 | a_influx_host, a_influx_port, a_influx_user, a_influx_pass, a_influx_db) 68 | 69 | 70 | def setup_custom_logger(name): 71 | formatter = logging.Formatter(fmt='%(asctime)s %(levelname)-8s %(message)s', 72 | datefmt='%Y-%m-%d %H:%M:%S') 73 | handler = logging.FileHandler(a_logfile, mode='a') 74 | handler.setFormatter(formatter) 75 | screen_handler = logging.StreamHandler(stream=sys.stdout) 76 | screen_handler.setFormatter(formatter) 77 | custom_logger = logging.getLogger(name) 78 | custom_logger.setLevel(a_loglevel) 79 | custom_logger.addHandler(handler) 80 | custom_logger.addHandler(screen_handler) 81 | return custom_logger 82 | 83 | 84 | logger = setup_custom_logger('apiscraper') 85 | 86 | 87 | class StateMonitor(object): 88 | """ Monitor all Tesla states.""" 89 | 90 | def __init__(self, tesla_email, tesla_password): 91 | self.requests = ("charge_state", "climate_state", "drive_state", 92 | "gui_settings", "vehicle_state") 93 | self.priority_requests = {1: ("drive_state",), 94 | 2: ("drive_state",), 95 | 4: ("charge_state", "drive_state",)} 96 | self.old_values = dict([(r, {}) for r in self.requests]) 97 | 98 | self.connection = teslajson.Connection(tesla_email, tesla_password) 99 | self.vehicle = self.connection.vehicles[a_tesla_car_idx] 100 | 101 | def refresh_vehicle(self): 102 | # refreshes the vehicle object 103 | logger.info(">> self.connection.refresh_vehicle()") 104 | self.connection.refresh_vehicle() 105 | self.vehicle = self.connection.vehicles[a_tesla_car_idx] 106 | 107 | def ongoing_activity_status(self): 108 | """ True if the car is not in park, or is actively charging ... """ 109 | shift = self.old_values['drive_state'].get('shift_state', '') 110 | old_speed = self.old_values['drive_state'].get('speed', 0) or 0 111 | if shift == "R" or shift == "D" or shift == "N" or old_speed > 0: 112 | return "Driving" 113 | if self.old_values['charge_state'].get('charging_state', '') in [ 114 | "Charging", "Starting"]: 115 | return "Charging" 116 | # If we just completed the charging, need to wait for voltage to 117 | # go down to zero too to avoid stale value in the DB. 118 | if (self.old_values['charge_state'].get('charging_state', '') == "Complete" or self.old_values[ 119 | 'charge_state'].get('charging_state', '') == "Stopped") \ 120 | or self.old_values['charge_state'].get('charger_voltage', 0) > 0 or self.old_values['charge_state'].get('charger_actual_current', 0) > 0: 121 | return "Charging" 122 | 123 | if self.old_values['climate_state'].get('is_climate_on', False): 124 | return "Conditioning" 125 | # When it's about time to start charging, we want to perform 126 | # several polling attempts to ensure we catch it starting even 127 | # when scraping is otherwise disabled 128 | if self.old_values['charge_state'].get('scheduled_charging_pending', False): 129 | scheduled_time = self.old_values['charge_state'].get('scheduled_charging_start_time', 0) 130 | if abs(scheduled_time - int(time.time())) <= 2: 131 | return "Charging" 132 | 133 | # If screen is on, the car is definitely not sleeping so no 134 | # harm in polling it as long as values are changing 135 | if self.old_values['vehicle_state'].get('center_display_state', 0) != 0: 136 | return "Screen On" 137 | 138 | return None 139 | 140 | def wake_up(self): 141 | """ mod """ 142 | global a_vin 143 | global a_display_name 144 | """ end mod """ 145 | """ Request wake up of car. """ 146 | delay = 1 147 | while True: 148 | try: 149 | result = self.vehicle.wake_up()["response"] 150 | logger.info("wake_up") 151 | for element in sorted(result): 152 | logger.debug(" %s=%s" % (element, result[element])) 153 | """ mod """ 154 | if element == "vin": 155 | a_vin = result[element] 156 | if element == "display_name": 157 | a_display_name = result[element] 158 | return 159 | except (KeyError, HTTPError, URLError) as details: 160 | delay *= 2 161 | logger.warning("HTTP Error:" + str(details)) 162 | logger.info("Waiting %d seconds before retrying." % delay) 163 | time.sleep(delay) 164 | 165 | def update_vehicle_from_response(self, response): 166 | # The other vehicle items are pretty much static. 167 | # We don't need to do any db insertions here, the main loop 168 | # would perform those for us. 169 | for item in ["state", "display_name"]: 170 | self.vehicle[item] = response[item] 171 | 172 | def request_state_group(self): 173 | global a_vin 174 | global a_display_name 175 | global a_ignore 176 | global last_data_from_tesla 177 | a_lat = None 178 | a_long = None 179 | # Request and process all Tesla states 180 | any_change = False 181 | logger.debug(">> Request vehicle data") 182 | r = self.vehicle.get("vehicle_data") 183 | 184 | self.update_vehicle_from_response(r['response']) 185 | 186 | for request in self.requests: 187 | header_printed = False 188 | result = r['response'][request] 189 | timestamp = result['timestamp'] 190 | last_data_from_tesla = timestamp 191 | if self.old_values[request].get('timestamp', '') == timestamp: 192 | break 193 | self.old_values[request]['timestamp'] = timestamp 194 | json_body = { 195 | "measurement": request, 196 | "tags": { 197 | "vin": a_vin, 198 | "display_name": a_display_name, 199 | }, 200 | "fields": { 201 | }, 202 | "time": int(timestamp) * 1000000, 203 | } 204 | for element in sorted(result): 205 | if element not in ( 206 | "timestamp", "gps_as_of", "left_temp_direction", "right_temp_direction", "charge_port_latch"): 207 | old_value = self.old_values[request].get(element, '') 208 | new_value = result[element] 209 | if element == "vehicle_name" and not new_value: 210 | continue 211 | if element == "native_latitude": 212 | a_lat = new_value 213 | if element == "native_longitude": 214 | a_long = new_value 215 | if new_value and old_value and ((element == "inside_temp") or (element == "outside_temp")): 216 | if abs(new_value - old_value) < 1.0: 217 | new_value = old_value 218 | logger.debug( 219 | "Only minimal temperature difference received. No change registered to avoid wakelock.") 220 | if new_value and old_value and ( 221 | (element == "battery_range") or 222 | (element == "est_battery_range") or 223 | (element == "ideal_battery_range") 224 | ): 225 | if abs(new_value - old_value) < 0.5: 226 | new_value = old_value 227 | logger.debug( 228 | "Only minimal range difference received. No change registered to avoid wakelock.") 229 | if (old_value == '') or ((new_value is not None) and (new_value != old_value)) or \ 230 | ((request == 'charge_state' and result['charging_state'] == 'Charging') and (element in ['charger_power', 'charger_voltage', 'charger_actual_current'])): 231 | logger.debug("Value Change, SG: " + request + ": Logging..." + element + 232 | ": old value: " + str(old_value) + ", new value: " + str(new_value)) 233 | if not header_printed: 234 | header_printed = True 235 | any_change = True 236 | if new_value is not None: 237 | if element not in a_ignore: 238 | if element in a_validity_checks and eval(a_validity_checks[element]["eval"]): 239 | logger.debug( 240 | "VALIDITY CHECK VIOLATED >>> " + element + ":" + a_validity_checks[element][ 241 | "eval"]) 242 | new_value = a_validity_checks[element]["set"] 243 | row = { element: new_value } 244 | json_body["fields"].update(row) 245 | self.old_values[request][element] = new_value 246 | if a_lat is not None and a_long is not None and a_resolve_elevation: 247 | # Fire and forget Elevation retrieval.. 248 | logger.debug("starting thread elevator: " + str(a_lat) + "/" + str(a_long) + "/" + str(timestamp)) 249 | elevator = threading.Thread(target=elevationtoinflux, 250 | args=( 251 | a_lat, a_long, a_vin, a_display_name, 252 | timestamp, influx_client, a_dry_run, logger)) 253 | # elevator.daemon = True 254 | elevator.setName("elevator") 255 | if not elevator.is_alive(): 256 | elevator.start() 257 | a_lat = None 258 | a_long = None 259 | 260 | if not a_dry_run and len(json_body["fields"])>0: 261 | logger.info("Writing Points to Influx: " + json_body["measurement"]) 262 | influx_client.write_points([json_body]) 263 | return any_change 264 | 265 | def check_states(self, interval): 266 | # Check all Tesla States 267 | any_change = False 268 | try: 269 | if self.request_state_group(): 270 | any_change = True 271 | else: 272 | shift = self.old_values['drive_state'].get('shift_state', '') 273 | if shift == "R" or shift == "D": 274 | # We are actively driving, does not matter we are 275 | # stopped at a traffic light or whatnot, 276 | # keep polling 277 | interval = 1 278 | any_change = True 279 | 280 | except (HTTPError, URLError) as exc: 281 | logger.info("HTTP Error: " + str(exc)) 282 | if a_allow_sleep == 1: 283 | return interval 284 | else: 285 | return -1 # re-initialize. 286 | 287 | if interval == 0: 288 | interval = 1 289 | 290 | # If we are charging at a supercharger, it's worth polling frequently 291 | # since the changes are fast. For regular charging 16 seconds 292 | # interval seems to be doing ok on my 72A/16kW charger, perhaps 293 | # we can even poll every 32 seconds on 40A and below? Polling 294 | # based on values changing is not good because there's constant +-1V 295 | # jitter on the source power that results in needless overhead 296 | if self.old_values['charge_state'].get('charging_state', '') == "Charging": 297 | if self.old_values['charge_state'].get('fast_charger_present', '') == "true": 298 | interval = 2 299 | else: 300 | interval = 16 301 | 302 | # If we are not charging (and not moving), then we can use the 303 | # usual logic to determine how often to poll based on how much 304 | # activity we see. 305 | else: 306 | if any_change: # there have been changes, reduce interval 307 | if interval > 1: 308 | interval /= 2 309 | else: # there haven't been any changes, increase interval to allow the car to fall asleep 310 | if interval < 2048: 311 | interval *= 2 312 | return interval 313 | 314 | 315 | ''' 316 | def last_state_report(f_vin): 317 | raw_query = "select time,state from vehicle_state where metric='state' and vin='{}' order by time desc limit 1" 318 | query = raw_query.format(f_vin) 319 | influx_result = influx_client.query(query) 320 | point = list(influx_result.get_points(measurement='vehicle_state')) 321 | return point[0] 322 | ''' 323 | 324 | 325 | # HTTP Thread Handler 326 | class ApiHandler(BaseHTTPRequestHandler): 327 | 328 | def do_HEAD(self): 329 | self.send_response(200) 330 | self.send_header("Content-type", "application/json") 331 | self.end_headers() 332 | 333 | def do_GET(self): 334 | if self.path == "/state" and self.headers.get('apikey') == a_api_key: 335 | self.send_response(200) 336 | busy_since_copy = busy_since 337 | if busy_since_copy: 338 | processing_time = int(time.time()) - busy_since_copy 339 | else: 340 | processing_time = 0 341 | api_response = [ 342 | { 343 | "result": "ok", 344 | "vin": a_vin, 345 | "scraperapiversion": scraper_api_version, 346 | "displayname": a_display_name, 347 | "state": is_asleep, 348 | "disablescraping": disableScrape, 349 | "carstate": car_active_state, 350 | "disabled_since": disabled_since, 351 | "interval": poll_interval, 352 | "lastdatafromtesla": int(last_data_from_tesla/1000), 353 | "busy": processing_time 354 | } 355 | ] 356 | else: 357 | self.send_response(400) 358 | api_response = [ 359 | { 360 | "result": "fail" 361 | } 362 | ] 363 | self.send_header("Content-type", "application/json") 364 | self.end_headers() 365 | byt = json.dumps(api_response, indent=4).encode() 366 | self.wfile.write(byt) 367 | 368 | # todo 369 | def do_POST(self): 370 | if self.path == "/switch" and self.headers.get('apikey') == a_api_key: 371 | content_length = int(self.headers['Content-Length']) 372 | body = self.rfile.read(content_length) 373 | post_command = json.loads(body.decode()) 374 | if post_command['command'] is not None: 375 | self.send_response(200) 376 | self.server.condition.acquire() 377 | self.server.pqueue.put(body) 378 | self.server.condition.notify() 379 | self.server.condition.release() 380 | else: 381 | self.send_response(401) 382 | else: 383 | self.send_response(400) 384 | self.end_headers() 385 | 386 | 387 | class QueuingHTTPServer(HTTPServer): 388 | def __init__(self, server_address, RequestHandlerClass, pqueue, cond, bind_and_activate=True): 389 | HTTPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate) 390 | self.pqueue = postq 391 | self.condition = cond 392 | 393 | 394 | def run_server(port, pq, cond): 395 | httpd = QueuingHTTPServer(('0.0.0.0', port), ApiHandler, pq, cond) 396 | while True: 397 | logger.debug("HANDLE: " + threading.current_thread().name) 398 | httpd.handle_request() 399 | 400 | 401 | if __name__ == "__main__": 402 | # Create Tesla API Interface 403 | try: 404 | state_monitor = StateMonitor(a_tesla_email, a_tesla_password) 405 | except: 406 | sys.exit("Failed to initialize Owner API") 407 | main_loop_count = 0 408 | 409 | # Create HTTP Server Thread 410 | if a_enable_api: 411 | thread = threading.Thread(target=run_server, args=(a_api_port, postq, http_condition)) 412 | thread.daemon = True 413 | try: 414 | thread.start() 415 | logger.info("HTTP Server Thread started on port " + str(a_api_port)) 416 | except KeyboardInterrupt: 417 | sys.exit(0) 418 | elif disableScrape: 419 | sys.exit("Configuration error: Scraping disabled and no api configured to enable. Bailing out") 420 | 421 | # Main Program Loop. messy.. 422 | while True: 423 | 424 | # We need to store this state in this global variable to ensure 425 | # HTTP thread is able to see it in real time as well. 426 | car_active_state = state_monitor.ongoing_activity_status() 427 | 428 | # Look if there's something from the Webservers Post Queue 429 | while not postq.empty(): 430 | req = json.loads(postq.get().decode()) 431 | command = req['command'] 432 | val = req['value'] 433 | logger.info("Command Queue not empty: " + command + ", value: " + str(val)) 434 | if command == "scrape": 435 | disableScrape = req['value'] 436 | if not disableScrape: 437 | logger.info("Resume Scrape requested") 438 | poll_interval = 1 439 | disabled_since = 0 440 | resume = True 441 | else: 442 | logger.info("Stop Scrape requested") 443 | disabled_since = (int(time.time())) 444 | if command == "oneshot": 445 | # Just override the car_active_state for a single 446 | # round of requests 447 | car_active_state = "oneshot request" 448 | logger.info("Oneshot update requested") 449 | if is_asleep == "asleep": 450 | logger.info("Waking the car up for the oneshot request") 451 | state_monitor.wake_up() 452 | resume = True 453 | 454 | if disableScrape is False or car_active_state is not None: 455 | busy_since = int(time.time()) 456 | # We cannot be sleeping with small poll interval for sure. 457 | # In fact can we be sleeping at all if scraping is enabled? 458 | if poll_interval >= 64 or resume: 459 | try: 460 | state_monitor.refresh_vehicle() 461 | except: 462 | logger.info("Hostname Exception Caught") 463 | # Car woke up 464 | if is_asleep == 'asleep' and state_monitor.vehicle['state'] == 'online': 465 | poll_interval = 0 466 | asleep_since = 0 467 | 468 | if state_monitor.vehicle['state'] == 'asleep' and is_asleep == 'online': 469 | asleep_since = time.time() 470 | 471 | is_asleep = state_monitor.vehicle['state'] 472 | a_vin = state_monitor.vehicle['vin'] 473 | a_display_name = state_monitor.vehicle['display_name'] 474 | ts = int(time.time()) * 1000000000 475 | state_body = [ 476 | { 477 | "measurement": 'vehicle_state', 478 | "tags": { 479 | "vin": state_monitor.vehicle['vin'], 480 | "display_name": state_monitor.vehicle['display_name'] 481 | }, 482 | "time": ts, 483 | "fields": { 484 | "state": is_asleep 485 | } 486 | } 487 | ] 488 | if not a_dry_run: 489 | influx_client.write_points(state_body) 490 | logger.debug("Car State: " + is_asleep + 491 | " Poll Interval: " + str(poll_interval)) 492 | if is_asleep == 'asleep' and a_allow_sleep == 1: 493 | logger.debug("Car is probably asleep, we let it sleep...") 494 | poll_interval = 64 495 | 496 | if poll_interval >= 0: 497 | if is_asleep != 'asleep': 498 | poll_interval = state_monitor.check_states(poll_interval) 499 | resume = False 500 | elif poll_interval < 0: 501 | state_monitor.wake_up() 502 | poll_interval = 1 503 | to_sleep = poll_interval 504 | processing_time = int(time.time()) - busy_since 505 | # If we spent too much time in processing, warn here 506 | # Reasons might be multiple like say slow DB or slow tesla api 507 | if processing_time > 10: 508 | logger.info("Too long processing loop: " + str(processing_time) + 509 | " seconds... Tesla server or DB slow?") 510 | logger.info("Asleep since: " + str(asleep_since) + 511 | " Sleeping for " + str(poll_interval) + " seconds..") 512 | busy_since = 0 513 | else: 514 | # If we have scheduled charging, lets wake up just in time 515 | # to catch that activity. 516 | if state_monitor.old_values['charge_state'].get('scheduled_charging_pending', False): 517 | to_sleep = state_monitor.old_values['charge_state'].get('scheduled_charging_start_time', 0) - int( 518 | time.time()) 519 | # This really should not happen 520 | if to_sleep <= 0: 521 | to_sleep = None 522 | else: 523 | logger.info("Going to sleep " + str(to_sleep) + " seconds until a scheduled charge") 524 | else: 525 | to_sleep = None 526 | 527 | # Look if there's something from the Webservers Post Queue 528 | http_condition.acquire() 529 | if not postq.empty(): 530 | # Need to turn around and do another round of processing 531 | # right away. 532 | http_condition.release() 533 | continue 534 | 535 | # Using to_sleep value ensures we don't miss the active car state here 536 | # even if disableScrape changed to true. Do not directly check it 537 | # here without checking for state_monitor.ongoing_activity_status() 538 | http_condition.wait(to_sleep) 539 | http_condition.release() 540 | # A wakeup here due to http request coming might cause a long sleep 541 | # to be interrupted early on and if no activity happened in the car, 542 | # double it, that's probably an ok condition that we should not care 543 | # too much about since it's guaranteed to be a "stop scraping" case 544 | # anyway ("start scraping" would reset poll interval to 1 and we'll 545 | # start quick polling anyway) 546 | # Same thing applies to the continue case above 547 | 548 | sys.stdout.flush() 549 | -------------------------------------------------------------------------------- /grafana-dashboards/Climate.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "DS_TESLA", 5 | "label": "tesla", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "influxdb", 9 | "pluginName": "InfluxDB" 10 | } 11 | ], 12 | "__requires": [ 13 | { 14 | "type": "grafana", 15 | "id": "grafana", 16 | "name": "Grafana", 17 | "version": "5.4.2" 18 | }, 19 | { 20 | "type": "panel", 21 | "id": "graph", 22 | "name": "Graph", 23 | "version": "5.0.0" 24 | }, 25 | { 26 | "type": "datasource", 27 | "id": "influxdb", 28 | "name": "InfluxDB", 29 | "version": "5.0.0" 30 | }, 31 | { 32 | "type": "panel", 33 | "id": "singlestat", 34 | "name": "Singlestat", 35 | "version": "5.0.0" 36 | } 37 | ], 38 | "annotations": { 39 | "list": [ 40 | { 41 | "builtIn": 1, 42 | "datasource": "-- Grafana --", 43 | "enable": true, 44 | "hide": true, 45 | "iconColor": "rgba(0, 211, 255, 1)", 46 | "name": "Annotations & Alerts", 47 | "type": "dashboard" 48 | } 49 | ] 50 | }, 51 | "editable": true, 52 | "gnetId": null, 53 | "graphTooltip": 0, 54 | "id": null, 55 | "iteration": 1548414552551, 56 | "links": [], 57 | "panels": [ 58 | { 59 | "aliasColors": {}, 60 | "bars": false, 61 | "dashLength": 10, 62 | "dashes": false, 63 | "datasource": "${DS_TESLA}", 64 | "fill": 1, 65 | "gridPos": { 66 | "h": 9, 67 | "w": 13, 68 | "x": 0, 69 | "y": 0 70 | }, 71 | "id": 2, 72 | "legend": { 73 | "avg": false, 74 | "current": false, 75 | "max": false, 76 | "min": false, 77 | "rightSide": false, 78 | "show": true, 79 | "total": false, 80 | "values": false 81 | }, 82 | "lines": true, 83 | "linewidth": 1, 84 | "links": [], 85 | "nullPointMode": "null", 86 | "percentage": false, 87 | "pointradius": 5, 88 | "points": false, 89 | "renderer": "flot", 90 | "seriesOverrides": [ 91 | { 92 | "alias": "fan_status", 93 | "yaxis": 2 94 | }, 95 | {} 96 | ], 97 | "spaceLength": 10, 98 | "stack": false, 99 | "steppedLine": false, 100 | "targets": [ 101 | { 102 | "alias": "fan_status", 103 | "groupBy": [ 104 | { 105 | "params": [ 106 | "1s" 107 | ], 108 | "type": "time" 109 | }, 110 | { 111 | "params": [ 112 | "previous" 113 | ], 114 | "type": "fill" 115 | } 116 | ], 117 | "measurement": "climate_state", 118 | "orderByTime": "ASC", 119 | "policy": "default", 120 | "refId": "A", 121 | "resultFormat": "time_series", 122 | "select": [ 123 | [ 124 | { 125 | "params": [ 126 | "fan_status" 127 | ], 128 | "type": "field" 129 | }, 130 | { 131 | "params": [], 132 | "type": "mean" 133 | } 134 | ] 135 | ], 136 | "tags": [ 137 | { 138 | "key": "display_name", 139 | "operator": "=~", 140 | "value": "/^$vehicle_name$/" 141 | } 142 | ] 143 | }, 144 | { 145 | "alias": "inside_temp", 146 | "groupBy": [ 147 | { 148 | "params": [ 149 | "1s" 150 | ], 151 | "type": "time" 152 | }, 153 | { 154 | "params": [ 155 | "previous" 156 | ], 157 | "type": "fill" 158 | } 159 | ], 160 | "measurement": "climate_state", 161 | "orderByTime": "ASC", 162 | "policy": "default", 163 | "refId": "B", 164 | "resultFormat": "time_series", 165 | "select": [ 166 | [ 167 | { 168 | "params": [ 169 | "inside_temp" 170 | ], 171 | "type": "field" 172 | }, 173 | { 174 | "params": [], 175 | "type": "mean" 176 | } 177 | ] 178 | ], 179 | "tags": [ 180 | { 181 | "key": "display_name", 182 | "operator": "=~", 183 | "value": "/^$vehicle_name$/" 184 | } 185 | ] 186 | }, 187 | { 188 | "alias": "outside_temp", 189 | "groupBy": [ 190 | { 191 | "params": [ 192 | "1s" 193 | ], 194 | "type": "time" 195 | }, 196 | { 197 | "params": [ 198 | "previous" 199 | ], 200 | "type": "fill" 201 | } 202 | ], 203 | "measurement": "climate_state", 204 | "orderByTime": "ASC", 205 | "policy": "default", 206 | "refId": "C", 207 | "resultFormat": "time_series", 208 | "select": [ 209 | [ 210 | { 211 | "params": [ 212 | "outside_temp" 213 | ], 214 | "type": "field" 215 | }, 216 | { 217 | "params": [], 218 | "type": "mean" 219 | } 220 | ] 221 | ], 222 | "tags": [ 223 | { 224 | "key": "display_name", 225 | "operator": "=~", 226 | "value": "/^$vehicle_name$/" 227 | } 228 | ] 229 | } 230 | ], 231 | "thresholds": [], 232 | "timeFrom": null, 233 | "timeRegions": [], 234 | "timeShift": null, 235 | "title": "FAN Status", 236 | "tooltip": { 237 | "shared": true, 238 | "sort": 0, 239 | "value_type": "individual" 240 | }, 241 | "type": "graph", 242 | "xaxis": { 243 | "buckets": null, 244 | "mode": "time", 245 | "name": null, 246 | "show": true, 247 | "values": [] 248 | }, 249 | "yaxes": [ 250 | { 251 | "format": "celsius", 252 | "label": null, 253 | "logBase": 1, 254 | "max": null, 255 | "min": null, 256 | "show": true 257 | }, 258 | { 259 | "format": "short", 260 | "label": "FAN Level", 261 | "logBase": 1, 262 | "max": null, 263 | "min": null, 264 | "show": true 265 | } 266 | ], 267 | "yaxis": { 268 | "align": false, 269 | "alignLevel": null 270 | } 271 | }, 272 | { 273 | "cacheTimeout": null, 274 | "colorBackground": false, 275 | "colorValue": false, 276 | "colors": [ 277 | "#299c46", 278 | "rgba(237, 129, 40, 0.89)", 279 | "#d44a3a" 280 | ], 281 | "datasource": "${DS_TESLA}", 282 | "format": "none", 283 | "gauge": { 284 | "maxValue": 100, 285 | "minValue": 0, 286 | "show": false, 287 | "thresholdLabels": false, 288 | "thresholdMarkers": true 289 | }, 290 | "gridPos": { 291 | "h": 3, 292 | "w": 3, 293 | "x": 13, 294 | "y": 0 295 | }, 296 | "id": 5, 297 | "interval": null, 298 | "links": [], 299 | "mappingType": 1, 300 | "mappingTypes": [ 301 | { 302 | "name": "value to text", 303 | "value": 1 304 | }, 305 | { 306 | "name": "range to text", 307 | "value": 2 308 | } 309 | ], 310 | "maxDataPoints": 100, 311 | "nullPointMode": "connected", 312 | "nullText": null, 313 | "postfix": "", 314 | "postfixFontSize": "50%", 315 | "prefix": "", 316 | "prefixFontSize": "50%", 317 | "rangeMaps": [ 318 | { 319 | "from": "null", 320 | "text": "N/A", 321 | "to": "null" 322 | } 323 | ], 324 | "sparkline": { 325 | "fillColor": "rgba(31, 118, 189, 0.18)", 326 | "full": false, 327 | "lineColor": "rgb(31, 120, 193)", 328 | "show": false 329 | }, 330 | "tableColumn": "", 331 | "targets": [ 332 | { 333 | "groupBy": [], 334 | "measurement": "climate_state", 335 | "orderByTime": "ASC", 336 | "policy": "default", 337 | "query": "SELECT \"value\" FROM \"climate_state\" WHERE (\"metric\" = 'seat_heater_right' AND \"display_name\" =~ /^$vehicle_name$/) AND $timeFilter", 338 | "rawQuery": false, 339 | "refId": "A", 340 | "resultFormat": "time_series", 341 | "select": [ 342 | [ 343 | { 344 | "params": [ 345 | "seat_heater_left" 346 | ], 347 | "type": "field" 348 | }, 349 | { 350 | "params": [], 351 | "type": "last" 352 | } 353 | ] 354 | ], 355 | "tags": [ 356 | { 357 | "key": "display_name", 358 | "operator": "=~", 359 | "value": "/^$vehicle_name$/" 360 | } 361 | ] 362 | } 363 | ], 364 | "thresholds": "", 365 | "title": "Seat Heater :: Left", 366 | "type": "singlestat", 367 | "valueFontSize": "80%", 368 | "valueMaps": [ 369 | { 370 | "op": "=", 371 | "text": "Off", 372 | "value": "0" 373 | }, 374 | { 375 | "op": "=", 376 | "text": "*", 377 | "value": "1" 378 | }, 379 | { 380 | "op": "=", 381 | "text": "**", 382 | "value": "2" 383 | }, 384 | { 385 | "op": "=", 386 | "text": "***", 387 | "value": "3" 388 | } 389 | ], 390 | "valueName": "current" 391 | }, 392 | { 393 | "cacheTimeout": null, 394 | "colorBackground": false, 395 | "colorValue": false, 396 | "colors": [ 397 | "#299c46", 398 | "rgba(237, 129, 40, 0.89)", 399 | "#d44a3a" 400 | ], 401 | "datasource": "${DS_TESLA}", 402 | "format": "short", 403 | "gauge": { 404 | "maxValue": 100, 405 | "minValue": 0, 406 | "show": false, 407 | "thresholdLabels": false, 408 | "thresholdMarkers": true 409 | }, 410 | "gridPos": { 411 | "h": 3, 412 | "w": 3, 413 | "x": 16, 414 | "y": 0 415 | }, 416 | "id": 11, 417 | "interval": null, 418 | "links": [], 419 | "mappingType": 1, 420 | "mappingTypes": [ 421 | { 422 | "name": "value to text", 423 | "value": 1 424 | }, 425 | { 426 | "name": "range to text", 427 | "value": 2 428 | } 429 | ], 430 | "maxDataPoints": 100, 431 | "nullPointMode": "connected", 432 | "nullText": null, 433 | "postfix": "", 434 | "postfixFontSize": "50%", 435 | "prefix": "", 436 | "prefixFontSize": "50%", 437 | "rangeMaps": [ 438 | { 439 | "from": "null", 440 | "text": "N/A", 441 | "to": "null" 442 | } 443 | ], 444 | "sparkline": { 445 | "fillColor": "rgba(31, 118, 189, 0.18)", 446 | "full": false, 447 | "lineColor": "rgb(31, 120, 193)", 448 | "show": false 449 | }, 450 | "tableColumn": "", 451 | "targets": [ 452 | { 453 | "groupBy": [], 454 | "measurement": "climate_state", 455 | "orderByTime": "ASC", 456 | "policy": "default", 457 | "query": "SELECT last(\"steering_wheel_heater\") FROM \"climate_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND $timeFilter", 458 | "rawQuery": true, 459 | "refId": "A", 460 | "resultFormat": "time_series", 461 | "select": [ 462 | [ 463 | { 464 | "params": [ 465 | "steering_wheel_heater" 466 | ], 467 | "type": "field" 468 | }, 469 | { 470 | "params": [], 471 | "type": "last" 472 | } 473 | ] 474 | ], 475 | "tags": [ 476 | { 477 | "key": "display_name", 478 | "operator": "=~", 479 | "value": "/^$vehicle_name$/" 480 | } 481 | ] 482 | } 483 | ], 484 | "thresholds": "", 485 | "title": "Steering Wheel", 486 | "type": "singlestat", 487 | "valueFontSize": "80%", 488 | "valueMaps": [ 489 | { 490 | "op": "=", 491 | "text": "On", 492 | "value": "1.0" 493 | }, 494 | { 495 | "op": "=", 496 | "text": "Off", 497 | "value": "null" 498 | } 499 | ], 500 | "valueName": "current" 501 | }, 502 | { 503 | "cacheTimeout": null, 504 | "colorBackground": false, 505 | "colorValue": false, 506 | "colors": [ 507 | "#299c46", 508 | "rgba(237, 129, 40, 0.89)", 509 | "#d44a3a" 510 | ], 511 | "datasource": "${DS_TESLA}", 512 | "format": "none", 513 | "gauge": { 514 | "maxValue": 100, 515 | "minValue": 0, 516 | "show": false, 517 | "thresholdLabels": false, 518 | "thresholdMarkers": true 519 | }, 520 | "gridPos": { 521 | "h": 3, 522 | "w": 3, 523 | "x": 19, 524 | "y": 0 525 | }, 526 | "id": 7, 527 | "interval": null, 528 | "links": [], 529 | "mappingType": 1, 530 | "mappingTypes": [ 531 | { 532 | "name": "value to text", 533 | "value": 1 534 | }, 535 | { 536 | "name": "range to text", 537 | "value": 2 538 | } 539 | ], 540 | "maxDataPoints": 100, 541 | "nullPointMode": "connected", 542 | "nullText": null, 543 | "postfix": "", 544 | "postfixFontSize": "50%", 545 | "prefix": "", 546 | "prefixFontSize": "50%", 547 | "rangeMaps": [ 548 | { 549 | "from": "null", 550 | "text": "N/A", 551 | "to": "null" 552 | } 553 | ], 554 | "sparkline": { 555 | "fillColor": "rgba(31, 118, 189, 0.18)", 556 | "full": false, 557 | "lineColor": "rgb(31, 120, 193)", 558 | "show": false 559 | }, 560 | "tableColumn": "", 561 | "targets": [ 562 | { 563 | "groupBy": [], 564 | "measurement": "climate_state", 565 | "orderByTime": "ASC", 566 | "policy": "default", 567 | "query": "SELECT \"value\" FROM \"climate_state\" WHERE (\"metric\" = 'seat_heater_right' AND \"display_name\" =~ /^$vehicle_name$/) AND $timeFilter", 568 | "rawQuery": false, 569 | "refId": "A", 570 | "resultFormat": "time_series", 571 | "select": [ 572 | [ 573 | { 574 | "params": [ 575 | "seat_heater_right" 576 | ], 577 | "type": "field" 578 | }, 579 | { 580 | "params": [], 581 | "type": "last" 582 | } 583 | ] 584 | ], 585 | "tags": [ 586 | { 587 | "key": "display_name", 588 | "operator": "=~", 589 | "value": "/^$vehicle_name$/" 590 | } 591 | ] 592 | } 593 | ], 594 | "thresholds": "", 595 | "title": "Seat Heater :: Right", 596 | "type": "singlestat", 597 | "valueFontSize": "80%", 598 | "valueMaps": [ 599 | { 600 | "op": "=", 601 | "text": "Off", 602 | "value": "0" 603 | }, 604 | { 605 | "op": "=", 606 | "text": "*", 607 | "value": "1" 608 | }, 609 | { 610 | "op": "=", 611 | "text": "**", 612 | "value": "2" 613 | }, 614 | { 615 | "op": "=", 616 | "text": "***", 617 | "value": "3" 618 | } 619 | ], 620 | "valueName": "current" 621 | }, 622 | { 623 | "cacheTimeout": null, 624 | "colorBackground": false, 625 | "colorValue": false, 626 | "colors": [ 627 | "#299c46", 628 | "rgba(237, 129, 40, 0.89)", 629 | "#d44a3a" 630 | ], 631 | "datasource": "${DS_TESLA}", 632 | "format": "none", 633 | "gauge": { 634 | "maxValue": 100, 635 | "minValue": 0, 636 | "show": false, 637 | "thresholdLabels": false, 638 | "thresholdMarkers": true 639 | }, 640 | "gridPos": { 641 | "h": 3, 642 | "w": 3, 643 | "x": 13, 644 | "y": 3 645 | }, 646 | "id": 6, 647 | "interval": null, 648 | "links": [], 649 | "mappingType": 1, 650 | "mappingTypes": [ 651 | { 652 | "name": "value to text", 653 | "value": 1 654 | }, 655 | { 656 | "name": "range to text", 657 | "value": 2 658 | } 659 | ], 660 | "maxDataPoints": 100, 661 | "nullPointMode": "connected", 662 | "nullText": null, 663 | "postfix": "", 664 | "postfixFontSize": "50%", 665 | "prefix": "", 666 | "prefixFontSize": "50%", 667 | "rangeMaps": [ 668 | { 669 | "from": "null", 670 | "text": "N/A", 671 | "to": "null" 672 | } 673 | ], 674 | "sparkline": { 675 | "fillColor": "rgba(31, 118, 189, 0.18)", 676 | "full": false, 677 | "lineColor": "rgb(31, 120, 193)", 678 | "show": false 679 | }, 680 | "tableColumn": "", 681 | "targets": [ 682 | { 683 | "groupBy": [], 684 | "measurement": "climate_state", 685 | "orderByTime": "ASC", 686 | "policy": "default", 687 | "query": "SELECT \"value\" FROM \"climate_state\" WHERE (\"metric\" = 'seat_heater_right' AND \"display_name\" =~ /^$vehicle_name$/) AND $timeFilter", 688 | "rawQuery": false, 689 | "refId": "A", 690 | "resultFormat": "time_series", 691 | "select": [ 692 | [ 693 | { 694 | "params": [ 695 | "seat_heater_rear_left" 696 | ], 697 | "type": "field" 698 | }, 699 | { 700 | "params": [], 701 | "type": "last" 702 | } 703 | ] 704 | ], 705 | "tags": [ 706 | { 707 | "key": "display_name", 708 | "operator": "=~", 709 | "value": "/^$vehicle_name$/" 710 | } 711 | ] 712 | } 713 | ], 714 | "thresholds": "", 715 | "title": "Seat Heater :: Rear Left", 716 | "type": "singlestat", 717 | "valueFontSize": "80%", 718 | "valueMaps": [ 719 | { 720 | "op": "=", 721 | "text": "Off", 722 | "value": "0" 723 | }, 724 | { 725 | "op": "=", 726 | "text": "*", 727 | "value": "1" 728 | }, 729 | { 730 | "op": "=", 731 | "text": "**", 732 | "value": "2" 733 | }, 734 | { 735 | "op": "=", 736 | "text": "***", 737 | "value": "3" 738 | } 739 | ], 740 | "valueName": "current" 741 | }, 742 | { 743 | "cacheTimeout": null, 744 | "colorBackground": false, 745 | "colorValue": false, 746 | "colors": [ 747 | "#299c46", 748 | "rgba(237, 129, 40, 0.89)", 749 | "#d44a3a" 750 | ], 751 | "datasource": "${DS_TESLA}", 752 | "format": "none", 753 | "gauge": { 754 | "maxValue": 100, 755 | "minValue": 0, 756 | "show": false, 757 | "thresholdLabels": false, 758 | "thresholdMarkers": true 759 | }, 760 | "gridPos": { 761 | "h": 3, 762 | "w": 3, 763 | "x": 16, 764 | "y": 3 765 | }, 766 | "id": 8, 767 | "interval": null, 768 | "links": [], 769 | "mappingType": 1, 770 | "mappingTypes": [ 771 | { 772 | "name": "value to text", 773 | "value": 1 774 | }, 775 | { 776 | "name": "range to text", 777 | "value": 2 778 | } 779 | ], 780 | "maxDataPoints": 100, 781 | "nullPointMode": "connected", 782 | "nullText": null, 783 | "postfix": "", 784 | "postfixFontSize": "50%", 785 | "prefix": "", 786 | "prefixFontSize": "50%", 787 | "rangeMaps": [ 788 | { 789 | "from": "null", 790 | "text": "N/A", 791 | "to": "null" 792 | } 793 | ], 794 | "sparkline": { 795 | "fillColor": "rgba(31, 118, 189, 0.18)", 796 | "full": false, 797 | "lineColor": "rgb(31, 120, 193)", 798 | "show": false 799 | }, 800 | "tableColumn": "", 801 | "targets": [ 802 | { 803 | "groupBy": [], 804 | "measurement": "climate_state", 805 | "orderByTime": "ASC", 806 | "policy": "default", 807 | "query": "SELECT \"value\" FROM \"climate_state\" WHERE (\"metric\" = 'seat_heater_right' AND \"display_name\" =~ /^$vehicle_name$/) AND $timeFilter", 808 | "rawQuery": false, 809 | "refId": "A", 810 | "resultFormat": "time_series", 811 | "select": [ 812 | [ 813 | { 814 | "params": [ 815 | "seat_heater_rear_center" 816 | ], 817 | "type": "field" 818 | }, 819 | { 820 | "params": [], 821 | "type": "last" 822 | } 823 | ] 824 | ], 825 | "tags": [ 826 | { 827 | "key": "display_name", 828 | "operator": "=~", 829 | "value": "/^$vehicle_name$/" 830 | } 831 | ] 832 | } 833 | ], 834 | "thresholds": "", 835 | "title": "Seat Heater :: Rear Center", 836 | "type": "singlestat", 837 | "valueFontSize": "80%", 838 | "valueMaps": [ 839 | { 840 | "op": "=", 841 | "text": "Off", 842 | "value": "0" 843 | }, 844 | { 845 | "op": "=", 846 | "text": "*", 847 | "value": "1" 848 | }, 849 | { 850 | "op": "=", 851 | "text": "**", 852 | "value": "2" 853 | }, 854 | { 855 | "op": "=", 856 | "text": "***", 857 | "value": "3" 858 | } 859 | ], 860 | "valueName": "current" 861 | }, 862 | { 863 | "cacheTimeout": null, 864 | "colorBackground": false, 865 | "colorValue": false, 866 | "colors": [ 867 | "#299c46", 868 | "rgba(237, 129, 40, 0.89)", 869 | "#d44a3a" 870 | ], 871 | "datasource": "${DS_TESLA}", 872 | "format": "none", 873 | "gauge": { 874 | "maxValue": 100, 875 | "minValue": 0, 876 | "show": false, 877 | "thresholdLabels": false, 878 | "thresholdMarkers": true 879 | }, 880 | "gridPos": { 881 | "h": 3, 882 | "w": 3, 883 | "x": 19, 884 | "y": 3 885 | }, 886 | "id": 9, 887 | "interval": null, 888 | "links": [], 889 | "mappingType": 1, 890 | "mappingTypes": [ 891 | { 892 | "name": "value to text", 893 | "value": 1 894 | }, 895 | { 896 | "name": "range to text", 897 | "value": 2 898 | } 899 | ], 900 | "maxDataPoints": 100, 901 | "nullPointMode": "connected", 902 | "nullText": null, 903 | "postfix": "", 904 | "postfixFontSize": "50%", 905 | "prefix": "", 906 | "prefixFontSize": "50%", 907 | "rangeMaps": [ 908 | { 909 | "from": "null", 910 | "text": "N/A", 911 | "to": "null" 912 | } 913 | ], 914 | "sparkline": { 915 | "fillColor": "rgba(31, 118, 189, 0.18)", 916 | "full": false, 917 | "lineColor": "rgb(31, 120, 193)", 918 | "show": false 919 | }, 920 | "tableColumn": "", 921 | "targets": [ 922 | { 923 | "groupBy": [], 924 | "measurement": "climate_state", 925 | "orderByTime": "ASC", 926 | "policy": "default", 927 | "query": "SELECT \"value\" FROM \"climate_state\" WHERE (\"metric\" = 'seat_heater_right' AND \"display_name\" =~ /^$vehicle_name$/) AND $timeFilter", 928 | "rawQuery": false, 929 | "refId": "A", 930 | "resultFormat": "time_series", 931 | "select": [ 932 | [ 933 | { 934 | "params": [ 935 | "seat_heater_rear_right" 936 | ], 937 | "type": "field" 938 | }, 939 | { 940 | "params": [], 941 | "type": "last" 942 | } 943 | ] 944 | ], 945 | "tags": [ 946 | { 947 | "key": "display_name", 948 | "operator": "=~", 949 | "value": "/^$vehicle_name$/" 950 | } 951 | ] 952 | } 953 | ], 954 | "thresholds": "", 955 | "title": "Seat Heater :: Rear Right", 956 | "type": "singlestat", 957 | "valueFontSize": "80%", 958 | "valueMaps": [ 959 | { 960 | "op": "=", 961 | "text": "Off", 962 | "value": "0" 963 | }, 964 | { 965 | "op": "=", 966 | "text": "*", 967 | "value": "1" 968 | }, 969 | { 970 | "op": "=", 971 | "text": "**", 972 | "value": "2" 973 | }, 974 | { 975 | "op": "=", 976 | "text": "***", 977 | "value": "3" 978 | } 979 | ], 980 | "valueName": "current" 981 | } 982 | ], 983 | "schemaVersion": 16, 984 | "style": "dark", 985 | "tags": [], 986 | "templating": { 987 | "list": [ 988 | { 989 | "allValue": null, 990 | "current": {}, 991 | "datasource": "${DS_TESLA}", 992 | "definition": "select distinct(display_name) from (select * from charge_state)", 993 | "hide": 0, 994 | "includeAll": false, 995 | "label": null, 996 | "multi": false, 997 | "name": "vehicle_name", 998 | "options": [], 999 | "query": "select distinct(display_name) from (select * from charge_state)", 1000 | "refresh": 1, 1001 | "regex": "", 1002 | "skipUrlSync": false, 1003 | "sort": 0, 1004 | "tagValuesQuery": "", 1005 | "tags": [], 1006 | "tagsQuery": "", 1007 | "type": "query", 1008 | "useTags": false 1009 | } 1010 | ] 1011 | }, 1012 | "time": { 1013 | "from": "now-6h", 1014 | "to": "now" 1015 | }, 1016 | "timepicker": { 1017 | "refresh_intervals": [ 1018 | "5s", 1019 | "10s", 1020 | "30s", 1021 | "1m", 1022 | "5m", 1023 | "15m", 1024 | "30m", 1025 | "1h", 1026 | "2h", 1027 | "1d" 1028 | ], 1029 | "time_options": [ 1030 | "5m", 1031 | "15m", 1032 | "1h", 1033 | "6h", 1034 | "12h", 1035 | "24h", 1036 | "2d", 1037 | "7d", 1038 | "30d" 1039 | ] 1040 | }, 1041 | "timezone": "", 1042 | "title": "Climate", 1043 | "uid": "i5jNBBUik", 1044 | "version": 46 1045 | } -------------------------------------------------------------------------------- /grafana-dashboards/Charging.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "DS_TESLA", 5 | "label": "tesla", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "influxdb", 9 | "pluginName": "InfluxDB" 10 | }, 11 | { 12 | "name": "VAR_RANGEUNIT", 13 | "type": "constant", 14 | "label": "rangeunit", 15 | "value": "km", 16 | "description": "" 17 | } 18 | ], 19 | "__requires": [ 20 | { 21 | "type": "grafana", 22 | "id": "grafana", 23 | "name": "Grafana", 24 | "version": "5.4.2" 25 | }, 26 | { 27 | "type": "panel", 28 | "id": "graph", 29 | "name": "Graph", 30 | "version": "5.0.0" 31 | }, 32 | { 33 | "type": "datasource", 34 | "id": "influxdb", 35 | "name": "InfluxDB", 36 | "version": "5.0.0" 37 | }, 38 | { 39 | "type": "panel", 40 | "id": "natel-discrete-panel", 41 | "name": "Discrete", 42 | "version": "0.0.9" 43 | }, 44 | { 45 | "type": "panel", 46 | "id": "singlestat", 47 | "name": "Singlestat", 48 | "version": "5.0.0" 49 | } 50 | ], 51 | "annotations": { 52 | "list": [ 53 | { 54 | "builtIn": 1, 55 | "datasource": "-- Grafana --", 56 | "enable": true, 57 | "hide": true, 58 | "iconColor": "rgba(0, 211, 255, 1)", 59 | "name": "Annotations & Alerts", 60 | "type": "dashboard" 61 | }, 62 | { 63 | "datasource": "${DS_TESLA}", 64 | "enable": false, 65 | "hide": false, 66 | "iconColor": "rgba(255, 96, 96, 1)", 67 | "limit": 100, 68 | "name": "charging_state", 69 | "query": "select charging_state as title,charging_state as description from charge_state where metric='charging_state' order by time asc", 70 | "showIn": 0, 71 | "tags": [], 72 | "tagsColumn": "", 73 | "textColumn": "", 74 | "type": "tags" 75 | } 76 | ] 77 | }, 78 | "editable": true, 79 | "gnetId": null, 80 | "graphTooltip": 2, 81 | "id": null, 82 | "iteration": 1548414540801, 83 | "links": [], 84 | "panels": [ 85 | { 86 | "cacheTimeout": null, 87 | "colorBackground": false, 88 | "colorValue": false, 89 | "colors": [ 90 | "#299c46", 91 | "rgba(237, 129, 40, 0.89)", 92 | "#d44a3a" 93 | ], 94 | "datasource": "${DS_TESLA}", 95 | "decimals": 2, 96 | "format": "none", 97 | "gauge": { 98 | "maxValue": 100, 99 | "minValue": 0, 100 | "show": false, 101 | "thresholdLabels": false, 102 | "thresholdMarkers": true 103 | }, 104 | "gridPos": { 105 | "h": 2, 106 | "w": 4, 107 | "x": 0, 108 | "y": 0 109 | }, 110 | "id": 20, 111 | "interval": null, 112 | "links": [], 113 | "mappingType": 1, 114 | "mappingTypes": [ 115 | { 116 | "name": "value to text", 117 | "value": 1 118 | }, 119 | { 120 | "name": "range to text", 121 | "value": 2 122 | } 123 | ], 124 | "maxDataPoints": 100, 125 | "nullPointMode": "connected", 126 | "nullText": null, 127 | "postfix": "$rangeunit", 128 | "postfixFontSize": "50%", 129 | "prefix": "Ideal", 130 | "prefixFontSize": "50%", 131 | "rangeMaps": [ 132 | { 133 | "from": "null", 134 | "text": "N/A", 135 | "to": "null" 136 | } 137 | ], 138 | "sparkline": { 139 | "fillColor": "rgba(31, 118, 189, 0.18)", 140 | "full": false, 141 | "lineColor": "rgb(31, 120, 193)", 142 | "show": false 143 | }, 144 | "tableColumn": "", 145 | "targets": [ 146 | { 147 | "groupBy": [], 148 | "measurement": "charge_state", 149 | "orderByTime": "ASC", 150 | "policy": "default", 151 | "refId": "A", 152 | "resultFormat": "time_series", 153 | "select": [ 154 | [ 155 | { 156 | "params": [ 157 | "ideal_battery_range" 158 | ], 159 | "type": "field" 160 | }, 161 | { 162 | "params": [], 163 | "type": "last" 164 | }, 165 | { 166 | "params": [ 167 | "*$rangefactor" 168 | ], 169 | "type": "math" 170 | } 171 | ] 172 | ], 173 | "tags": [ 174 | { 175 | "key": "display_name", 176 | "operator": "=~", 177 | "value": "/^$vehicle_name$/" 178 | } 179 | ] 180 | } 181 | ], 182 | "thresholds": "", 183 | "title": "Range", 184 | "type": "singlestat", 185 | "valueFontSize": "80%", 186 | "valueMaps": [ 187 | { 188 | "op": "=", 189 | "text": "N/A", 190 | "value": "null" 191 | } 192 | ], 193 | "valueName": "avg" 194 | }, 195 | { 196 | "cacheTimeout": null, 197 | "colorBackground": false, 198 | "colorValue": false, 199 | "colors": [ 200 | "#299c46", 201 | "rgba(237, 129, 40, 0.89)", 202 | "#d44a3a" 203 | ], 204 | "datasource": "${DS_TESLA}", 205 | "decimals": 2, 206 | "format": "none", 207 | "gauge": { 208 | "maxValue": 100, 209 | "minValue": 0, 210 | "show": false, 211 | "thresholdLabels": false, 212 | "thresholdMarkers": true 213 | }, 214 | "gridPos": { 215 | "h": 2, 216 | "w": 4, 217 | "x": 4, 218 | "y": 0 219 | }, 220 | "id": 18, 221 | "interval": null, 222 | "links": [], 223 | "mappingType": 1, 224 | "mappingTypes": [ 225 | { 226 | "name": "value to text", 227 | "value": 1 228 | }, 229 | { 230 | "name": "range to text", 231 | "value": 2 232 | } 233 | ], 234 | "maxDataPoints": 100, 235 | "nullPointMode": "connected", 236 | "nullText": null, 237 | "postfix": "$rangeunit", 238 | "postfixFontSize": "50%", 239 | "prefix": "Est.", 240 | "prefixFontSize": "50%", 241 | "rangeMaps": [ 242 | { 243 | "from": "null", 244 | "text": "N/A", 245 | "to": "null" 246 | } 247 | ], 248 | "sparkline": { 249 | "fillColor": "rgba(31, 118, 189, 0.18)", 250 | "full": false, 251 | "lineColor": "rgb(31, 120, 193)", 252 | "show": false 253 | }, 254 | "tableColumn": "", 255 | "targets": [ 256 | { 257 | "groupBy": [], 258 | "measurement": "charge_state", 259 | "orderByTime": "ASC", 260 | "policy": "default", 261 | "query": "SELECT last(\"est_battery_range\") FROM \"charge_state\" WHERE (\"metric\" = 'est_battery_range') AND $timeFilter", 262 | "rawQuery": false, 263 | "refId": "A", 264 | "resultFormat": "time_series", 265 | "select": [ 266 | [ 267 | { 268 | "params": [ 269 | "est_battery_range" 270 | ], 271 | "type": "field" 272 | }, 273 | { 274 | "params": [], 275 | "type": "last" 276 | }, 277 | { 278 | "params": [ 279 | "*$rangefactor" 280 | ], 281 | "type": "math" 282 | } 283 | ] 284 | ], 285 | "tags": [ 286 | { 287 | "key": "display_name", 288 | "operator": "=", 289 | "value": "/^$vehicle_name$/" 290 | } 291 | ] 292 | } 293 | ], 294 | "thresholds": "", 295 | "title": "Range", 296 | "type": "singlestat", 297 | "valueFontSize": "80%", 298 | "valueMaps": [ 299 | { 300 | "op": "=", 301 | "text": "N/A", 302 | "value": "null" 303 | } 304 | ], 305 | "valueName": "avg" 306 | }, 307 | { 308 | "cacheTimeout": null, 309 | "colorBackground": false, 310 | "colorValue": false, 311 | "colors": [ 312 | "#299c46", 313 | "rgba(237, 129, 40, 0.89)", 314 | "#d44a3a" 315 | ], 316 | "datasource": "${DS_TESLA}", 317 | "decimals": 2, 318 | "format": "none", 319 | "gauge": { 320 | "maxValue": 100, 321 | "minValue": 0, 322 | "show": false, 323 | "thresholdLabels": false, 324 | "thresholdMarkers": true 325 | }, 326 | "gridPos": { 327 | "h": 2, 328 | "w": 4, 329 | "x": 8, 330 | "y": 0 331 | }, 332 | "id": 19, 333 | "interval": null, 334 | "links": [], 335 | "mappingType": 1, 336 | "mappingTypes": [ 337 | { 338 | "name": "value to text", 339 | "value": 1 340 | }, 341 | { 342 | "name": "range to text", 343 | "value": 2 344 | } 345 | ], 346 | "maxDataPoints": 100, 347 | "nullPointMode": "connected", 348 | "nullText": null, 349 | "postfix": "$rangeunit", 350 | "postfixFontSize": "50%", 351 | "prefix": "Rated", 352 | "prefixFontSize": "50%", 353 | "rangeMaps": [ 354 | { 355 | "from": "null", 356 | "text": "N/A", 357 | "to": "null" 358 | } 359 | ], 360 | "sparkline": { 361 | "fillColor": "rgba(31, 118, 189, 0.18)", 362 | "full": false, 363 | "lineColor": "rgb(31, 120, 193)", 364 | "show": false 365 | }, 366 | "tableColumn": "", 367 | "targets": [ 368 | { 369 | "groupBy": [], 370 | "measurement": "charge_state", 371 | "orderByTime": "ASC", 372 | "policy": "default", 373 | "query": "SELECT last(\"battery_range\") *$rangefactor FROM \"charge_state\" WHERE (\"metric\" = 'battery_range') AND $timeFilter", 374 | "rawQuery": false, 375 | "refId": "A", 376 | "resultFormat": "time_series", 377 | "select": [ 378 | [ 379 | { 380 | "params": [ 381 | "battery_range" 382 | ], 383 | "type": "field" 384 | }, 385 | { 386 | "params": [], 387 | "type": "last" 388 | }, 389 | { 390 | "params": [ 391 | "*$rangefactor" 392 | ], 393 | "type": "math" 394 | } 395 | ] 396 | ], 397 | "tags": [ 398 | { 399 | "key": "display_name", 400 | "operator": "=~", 401 | "value": "/^$vehicle_name$/" 402 | } 403 | ] 404 | } 405 | ], 406 | "thresholds": "", 407 | "title": "Range", 408 | "type": "singlestat", 409 | "valueFontSize": "80%", 410 | "valueMaps": [ 411 | { 412 | "op": "=", 413 | "text": "N/A", 414 | "value": "null" 415 | } 416 | ], 417 | "valueName": "avg" 418 | }, 419 | { 420 | "cacheTimeout": null, 421 | "colorBackground": false, 422 | "colorValue": false, 423 | "colors": [ 424 | "#299c46", 425 | "rgba(237, 129, 40, 0.89)", 426 | "#d44a3a" 427 | ], 428 | "datasource": "${DS_TESLA}", 429 | "description": "Energy added on last Charge", 430 | "format": "kwatth", 431 | "gauge": { 432 | "maxValue": 100, 433 | "minValue": 0, 434 | "show": false, 435 | "thresholdLabels": false, 436 | "thresholdMarkers": true 437 | }, 438 | "gridPos": { 439 | "h": 2, 440 | "w": 3, 441 | "x": 12, 442 | "y": 0 443 | }, 444 | "id": 32, 445 | "interval": null, 446 | "links": [], 447 | "mappingType": 1, 448 | "mappingTypes": [ 449 | { 450 | "name": "value to text", 451 | "value": 1 452 | }, 453 | { 454 | "name": "range to text", 455 | "value": 2 456 | } 457 | ], 458 | "maxDataPoints": 100, 459 | "nullPointMode": "connected", 460 | "nullText": null, 461 | "postfix": "", 462 | "postfixFontSize": "50%", 463 | "prefix": "", 464 | "prefixFontSize": "50%", 465 | "rangeMaps": [ 466 | { 467 | "from": "null", 468 | "text": "N/A", 469 | "to": "null" 470 | } 471 | ], 472 | "sparkline": { 473 | "fillColor": "rgba(31, 118, 189, 0.18)", 474 | "full": false, 475 | "lineColor": "rgb(31, 120, 193)", 476 | "show": false 477 | }, 478 | "tableColumn": "", 479 | "targets": [ 480 | { 481 | "groupBy": [], 482 | "measurement": "charge_state", 483 | "orderByTime": "ASC", 484 | "policy": "default", 485 | "refId": "A", 486 | "resultFormat": "time_series", 487 | "select": [ 488 | [ 489 | { 490 | "params": [ 491 | "charge_energy_added" 492 | ], 493 | "type": "field" 494 | } 495 | ] 496 | ], 497 | "tags": [ 498 | { 499 | "key": "display_name", 500 | "operator": "=~", 501 | "value": "/^$vehicle_name$/" 502 | } 503 | ] 504 | } 505 | ], 506 | "thresholds": "", 507 | "title": "Energy added", 508 | "type": "singlestat", 509 | "valueFontSize": "80%", 510 | "valueMaps": [ 511 | { 512 | "op": "=", 513 | "text": "N/A", 514 | "value": "null" 515 | } 516 | ], 517 | "valueName": "avg" 518 | }, 519 | { 520 | "cacheTimeout": null, 521 | "colorBackground": false, 522 | "colorValue": false, 523 | "colors": [ 524 | "#299c46", 525 | "rgba(237, 129, 40, 0.89)", 526 | "#d44a3a" 527 | ], 528 | "datasource": "${DS_TESLA}", 529 | "decimals": 2, 530 | "format": "h", 531 | "gauge": { 532 | "maxValue": 100, 533 | "minValue": 0, 534 | "show": false, 535 | "thresholdLabels": false, 536 | "thresholdMarkers": true 537 | }, 538 | "gridPos": { 539 | "h": 2, 540 | "w": 3, 541 | "x": 15, 542 | "y": 0 543 | }, 544 | "hideTimeOverride": false, 545 | "id": 30, 546 | "interval": null, 547 | "links": [], 548 | "mappingType": 1, 549 | "mappingTypes": [ 550 | { 551 | "name": "value to text", 552 | "value": 1 553 | }, 554 | { 555 | "name": "range to text", 556 | "value": 2 557 | } 558 | ], 559 | "maxDataPoints": 100, 560 | "nullPointMode": "connected", 561 | "nullText": null, 562 | "postfix": "", 563 | "postfixFontSize": "50%", 564 | "prefix": "", 565 | "prefixFontSize": "50%", 566 | "rangeMaps": [ 567 | { 568 | "from": "null", 569 | "text": "N/A", 570 | "to": "null" 571 | } 572 | ], 573 | "sparkline": { 574 | "fillColor": "rgba(31, 118, 189, 0.18)", 575 | "full": false, 576 | "lineColor": "rgb(31, 120, 193)", 577 | "show": false 578 | }, 579 | "tableColumn": "last", 580 | "targets": [ 581 | { 582 | "alias": "", 583 | "groupBy": [], 584 | "measurement": "charge_state", 585 | "orderByTime": "ASC", 586 | "policy": "default", 587 | "refId": "A", 588 | "resultFormat": "time_series", 589 | "select": [ 590 | [ 591 | { 592 | "params": [ 593 | "time_to_full_charge" 594 | ], 595 | "type": "field" 596 | } 597 | ] 598 | ], 599 | "tags": [ 600 | { 601 | "key": "display_name", 602 | "operator": "=~", 603 | "value": "/^$vehicle_name$/" 604 | } 605 | ] 606 | } 607 | ], 608 | "thresholds": "", 609 | "title": "Time to Full at current Rate", 610 | "transparent": false, 611 | "type": "singlestat", 612 | "valueFontSize": "80%", 613 | "valueMaps": [], 614 | "valueName": "current" 615 | }, 616 | { 617 | "cacheTimeout": null, 618 | "colorBackground": false, 619 | "colorValue": false, 620 | "colors": [ 621 | "#299c46", 622 | "rgba(237, 129, 40, 0.89)", 623 | "#d44a3a" 624 | ], 625 | "datasource": "${DS_TESLA}", 626 | "format": "none", 627 | "gauge": { 628 | "maxValue": 100, 629 | "minValue": 0, 630 | "show": false, 631 | "thresholdLabels": false, 632 | "thresholdMarkers": true 633 | }, 634 | "gridPos": { 635 | "h": 2, 636 | "w": 3, 637 | "x": 18, 638 | "y": 0 639 | }, 640 | "id": 4, 641 | "interval": null, 642 | "links": [], 643 | "mappingType": 1, 644 | "mappingTypes": [ 645 | { 646 | "name": "value to text", 647 | "value": 1 648 | }, 649 | { 650 | "name": "range to text", 651 | "value": 2 652 | } 653 | ], 654 | "maxDataPoints": 100, 655 | "nullPointMode": "connected", 656 | "nullText": null, 657 | "postfix": "", 658 | "postfixFontSize": "50%", 659 | "prefix": "", 660 | "prefixFontSize": "50%", 661 | "rangeMaps": [ 662 | { 663 | "from": "null", 664 | "text": "N/A", 665 | "to": "null" 666 | } 667 | ], 668 | "sparkline": { 669 | "fillColor": "rgba(31, 118, 189, 0.18)", 670 | "full": false, 671 | "lineColor": "rgb(31, 120, 193)", 672 | "show": false 673 | }, 674 | "tableColumn": "", 675 | "targets": [ 676 | { 677 | "groupBy": [], 678 | "measurement": "charge_state", 679 | "orderByTime": "ASC", 680 | "policy": "default", 681 | "refId": "A", 682 | "resultFormat": "time_series", 683 | "select": [ 684 | [ 685 | { 686 | "params": [ 687 | "charger_phases" 688 | ], 689 | "type": "field" 690 | }, 691 | { 692 | "params": [], 693 | "type": "last" 694 | } 695 | ] 696 | ], 697 | "tags": [ 698 | { 699 | "key": "display_name", 700 | "operator": "=~", 701 | "value": "/^$vehicle_name$/" 702 | } 703 | ] 704 | } 705 | ], 706 | "thresholds": "", 707 | "title": "AC Charger Phases", 708 | "type": "singlestat", 709 | "valueFontSize": "80%", 710 | "valueMaps": [ 711 | { 712 | "op": "=", 713 | "text": "N/A", 714 | "value": "null" 715 | } 716 | ], 717 | "valueName": "avg" 718 | }, 719 | { 720 | "cacheTimeout": null, 721 | "colorBackground": false, 722 | "colorValue": false, 723 | "colors": [ 724 | "#299c46", 725 | "rgba(237, 129, 40, 0.89)", 726 | "#d44a3a" 727 | ], 728 | "datasource": "${DS_TESLA}", 729 | "format": "amp", 730 | "gauge": { 731 | "maxValue": 100, 732 | "minValue": 0, 733 | "show": false, 734 | "thresholdLabels": false, 735 | "thresholdMarkers": true 736 | }, 737 | "gridPos": { 738 | "h": 2, 739 | "w": 3, 740 | "x": 21, 741 | "y": 0 742 | }, 743 | "id": 10, 744 | "interval": null, 745 | "links": [], 746 | "mappingType": 1, 747 | "mappingTypes": [ 748 | { 749 | "name": "value to text", 750 | "value": 1 751 | }, 752 | { 753 | "name": "range to text", 754 | "value": 2 755 | } 756 | ], 757 | "maxDataPoints": 100, 758 | "nullPointMode": "connected", 759 | "nullText": null, 760 | "postfix": "", 761 | "postfixFontSize": "50%", 762 | "prefix": "", 763 | "prefixFontSize": "50%", 764 | "rangeMaps": [ 765 | { 766 | "from": "null", 767 | "text": "N/A", 768 | "to": "null" 769 | } 770 | ], 771 | "sparkline": { 772 | "fillColor": "rgba(31, 118, 189, 0.18)", 773 | "full": false, 774 | "lineColor": "rgb(31, 120, 193)", 775 | "show": false 776 | }, 777 | "tableColumn": "", 778 | "targets": [ 779 | { 780 | "groupBy": [], 781 | "measurement": "charge_state", 782 | "orderByTime": "ASC", 783 | "policy": "default", 784 | "refId": "A", 785 | "resultFormat": "time_series", 786 | "select": [ 787 | [ 788 | { 789 | "params": [ 790 | "charger_actual_current" 791 | ], 792 | "type": "field" 793 | }, 794 | { 795 | "params": [], 796 | "type": "last" 797 | } 798 | ] 799 | ], 800 | "tags": [ 801 | { 802 | "key": "display_name", 803 | "operator": "=~", 804 | "value": "/^$vehicle_name$/" 805 | } 806 | ] 807 | } 808 | ], 809 | "thresholds": "", 810 | "title": "AC Charger Actual Current", 811 | "type": "singlestat", 812 | "valueFontSize": "80%", 813 | "valueMaps": [ 814 | { 815 | "op": "=", 816 | "text": "N/A", 817 | "value": "null" 818 | } 819 | ], 820 | "valueName": "avg" 821 | }, 822 | { 823 | "cacheTimeout": null, 824 | "colorBackground": false, 825 | "colorValue": true, 826 | "colors": [ 827 | "#e24d42", 828 | "#508642", 829 | "#e5ac0e" 830 | ], 831 | "datasource": "${DS_TESLA}", 832 | "description": "Voltage supplied by Charger", 833 | "format": "volt", 834 | "gauge": { 835 | "maxValue": 260, 836 | "minValue": 0, 837 | "show": true, 838 | "thresholdLabels": false, 839 | "thresholdMarkers": true 840 | }, 841 | "gridPos": { 842 | "h": 5, 843 | "w": 4, 844 | "x": 0, 845 | "y": 2 846 | }, 847 | "id": 2, 848 | "interval": null, 849 | "links": [], 850 | "mappingType": 1, 851 | "mappingTypes": [ 852 | { 853 | "name": "value to text", 854 | "value": 1 855 | }, 856 | { 857 | "name": "range to text", 858 | "value": 2 859 | } 860 | ], 861 | "maxDataPoints": 100, 862 | "nullPointMode": "connected", 863 | "nullText": null, 864 | "postfix": "", 865 | "postfixFontSize": "50%", 866 | "prefix": "", 867 | "prefixFontSize": "50%", 868 | "rangeMaps": [ 869 | { 870 | "from": "null", 871 | "text": "N/A", 872 | "to": "null" 873 | } 874 | ], 875 | "sparkline": { 876 | "fillColor": "rgba(31, 118, 189, 0.18)", 877 | "full": false, 878 | "lineColor": "rgb(31, 120, 193)", 879 | "show": false 880 | }, 881 | "tableColumn": "", 882 | "targets": [ 883 | { 884 | "groupBy": [], 885 | "measurement": "charge_state", 886 | "orderByTime": "ASC", 887 | "policy": "default", 888 | "query": "SELECT last(\"charger_voltage\") FROM \"charge_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) ", 889 | "rawQuery": true, 890 | "refId": "A", 891 | "resultFormat": "time_series", 892 | "select": [ 893 | [ 894 | { 895 | "params": [ 896 | "charger_voltage" 897 | ], 898 | "type": "field" 899 | }, 900 | { 901 | "params": [], 902 | "type": "last" 903 | } 904 | ] 905 | ], 906 | "tags": [ 907 | { 908 | "key": "metric", 909 | "operator": "=", 910 | "value": "charger_voltage" 911 | }, 912 | { 913 | "condition": "AND", 914 | "key": "display_name", 915 | "operator": "=~", 916 | "value": "/^$vehicle_name$/" 917 | } 918 | ] 919 | } 920 | ], 921 | "thresholds": "196,240", 922 | "title": "Charger Voltage", 923 | "type": "singlestat", 924 | "valueFontSize": "80%", 925 | "valueMaps": [ 926 | { 927 | "op": "=", 928 | "text": "N/A", 929 | "value": "null" 930 | } 931 | ], 932 | "valueName": "current" 933 | }, 934 | { 935 | "aliasColors": {}, 936 | "bars": false, 937 | "dashLength": 10, 938 | "dashes": false, 939 | "datasource": "${DS_TESLA}", 940 | "fill": 1, 941 | "gridPos": { 942 | "h": 5, 943 | "w": 20, 944 | "x": 4, 945 | "y": 2 946 | }, 947 | "id": 12, 948 | "legend": { 949 | "avg": false, 950 | "current": false, 951 | "hideEmpty": false, 952 | "hideZero": false, 953 | "max": false, 954 | "min": false, 955 | "show": true, 956 | "total": false, 957 | "values": false 958 | }, 959 | "lines": true, 960 | "linewidth": 1, 961 | "links": [], 962 | "nullPointMode": "null", 963 | "percentage": false, 964 | "pointradius": 5, 965 | "points": false, 966 | "renderer": "flot", 967 | "seriesOverrides": [], 968 | "spaceLength": 10, 969 | "stack": false, 970 | "steppedLine": false, 971 | "targets": [ 972 | { 973 | "alias": "charger_voltage", 974 | "groupBy": [ 975 | { 976 | "params": [ 977 | "1s" 978 | ], 979 | "type": "time" 980 | }, 981 | { 982 | "params": [ 983 | "previous" 984 | ], 985 | "type": "fill" 986 | } 987 | ], 988 | "measurement": "charge_state", 989 | "orderByTime": "ASC", 990 | "policy": "default", 991 | "refId": "A", 992 | "resultFormat": "time_series", 993 | "select": [ 994 | [ 995 | { 996 | "params": [ 997 | "charger_voltage" 998 | ], 999 | "type": "field" 1000 | }, 1001 | { 1002 | "params": [], 1003 | "type": "mean" 1004 | } 1005 | ] 1006 | ], 1007 | "tags": [ 1008 | { 1009 | "key": "display_name", 1010 | "operator": "=~", 1011 | "value": "/^$vehicle_name$/" 1012 | } 1013 | ] 1014 | } 1015 | ], 1016 | "thresholds": [], 1017 | "timeFrom": null, 1018 | "timeRegions": [], 1019 | "timeShift": null, 1020 | "title": "Charger Voltage", 1021 | "tooltip": { 1022 | "shared": true, 1023 | "sort": 0, 1024 | "value_type": "individual" 1025 | }, 1026 | "type": "graph", 1027 | "xaxis": { 1028 | "buckets": null, 1029 | "mode": "time", 1030 | "name": null, 1031 | "show": true, 1032 | "values": [] 1033 | }, 1034 | "yaxes": [ 1035 | { 1036 | "format": "volt", 1037 | "label": null, 1038 | "logBase": 1, 1039 | "max": null, 1040 | "min": "0", 1041 | "show": true 1042 | }, 1043 | { 1044 | "format": "short", 1045 | "label": null, 1046 | "logBase": 1, 1047 | "max": null, 1048 | "min": null, 1049 | "show": true 1050 | } 1051 | ], 1052 | "yaxis": { 1053 | "align": false, 1054 | "alignLevel": null 1055 | } 1056 | }, 1057 | { 1058 | "cacheTimeout": null, 1059 | "colorBackground": false, 1060 | "colorValue": true, 1061 | "colors": [ 1062 | "#e24d42", 1063 | "#e5ac0e", 1064 | "#508642" 1065 | ], 1066 | "datasource": "${DS_TESLA}", 1067 | "description": "Current Battery Level", 1068 | "format": "percent", 1069 | "gauge": { 1070 | "maxValue": 100, 1071 | "minValue": 0, 1072 | "show": true, 1073 | "thresholdLabels": false, 1074 | "thresholdMarkers": true 1075 | }, 1076 | "gridPos": { 1077 | "h": 5, 1078 | "w": 4, 1079 | "x": 0, 1080 | "y": 7 1081 | }, 1082 | "id": 14, 1083 | "interval": null, 1084 | "links": [], 1085 | "mappingType": 1, 1086 | "mappingTypes": [ 1087 | { 1088 | "name": "value to text", 1089 | "value": 1 1090 | }, 1091 | { 1092 | "name": "range to text", 1093 | "value": 2 1094 | } 1095 | ], 1096 | "maxDataPoints": 100, 1097 | "nullPointMode": "connected", 1098 | "nullText": null, 1099 | "postfix": "", 1100 | "postfixFontSize": "50%", 1101 | "prefix": "", 1102 | "prefixFontSize": "50%", 1103 | "rangeMaps": [ 1104 | { 1105 | "from": "null", 1106 | "text": "N/A", 1107 | "to": "null" 1108 | } 1109 | ], 1110 | "sparkline": { 1111 | "fillColor": "rgba(31, 118, 189, 0.18)", 1112 | "full": false, 1113 | "lineColor": "rgb(31, 120, 193)", 1114 | "show": false 1115 | }, 1116 | "tableColumn": "", 1117 | "targets": [ 1118 | { 1119 | "groupBy": [], 1120 | "measurement": "charge_state", 1121 | "orderByTime": "ASC", 1122 | "policy": "default", 1123 | "refId": "A", 1124 | "resultFormat": "time_series", 1125 | "select": [ 1126 | [ 1127 | { 1128 | "params": [ 1129 | "battery_level" 1130 | ], 1131 | "type": "field" 1132 | }, 1133 | { 1134 | "params": [], 1135 | "type": "last" 1136 | } 1137 | ] 1138 | ], 1139 | "tags": [ 1140 | { 1141 | "key": "display_name", 1142 | "operator": "=~", 1143 | "value": "/^$vehicle_name$/" 1144 | } 1145 | ] 1146 | } 1147 | ], 1148 | "thresholds": "8,20", 1149 | "title": "Battery Level", 1150 | "type": "singlestat", 1151 | "valueFontSize": "80%", 1152 | "valueMaps": [ 1153 | { 1154 | "op": "=", 1155 | "text": "N/A", 1156 | "value": "null" 1157 | } 1158 | ], 1159 | "valueName": "avg" 1160 | }, 1161 | { 1162 | "aliasColors": {}, 1163 | "bars": false, 1164 | "dashLength": 10, 1165 | "dashes": false, 1166 | "datasource": "${DS_TESLA}", 1167 | "fill": 1, 1168 | "gridPos": { 1169 | "h": 5, 1170 | "w": 20, 1171 | "x": 4, 1172 | "y": 7 1173 | }, 1174 | "id": 24, 1175 | "legend": { 1176 | "avg": false, 1177 | "current": false, 1178 | "max": false, 1179 | "min": false, 1180 | "show": true, 1181 | "total": false, 1182 | "values": false 1183 | }, 1184 | "lines": true, 1185 | "linewidth": 1, 1186 | "links": [], 1187 | "nullPointMode": "null", 1188 | "percentage": false, 1189 | "pointradius": 5, 1190 | "points": false, 1191 | "renderer": "flot", 1192 | "seriesOverrides": [], 1193 | "spaceLength": 10, 1194 | "stack": false, 1195 | "steppedLine": false, 1196 | "targets": [ 1197 | { 1198 | "alias": "battery_level", 1199 | "groupBy": [ 1200 | { 1201 | "params": [ 1202 | "$__interval" 1203 | ], 1204 | "type": "time" 1205 | }, 1206 | { 1207 | "params": [ 1208 | "linear" 1209 | ], 1210 | "type": "fill" 1211 | } 1212 | ], 1213 | "measurement": "charge_state", 1214 | "orderByTime": "ASC", 1215 | "policy": "default", 1216 | "query": "SELECT mean(\"battery_level\") FROM \"charge_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND $timeFilter GROUP BY time($__interval) fill(linear)", 1217 | "rawQuery": true, 1218 | "refId": "A", 1219 | "resultFormat": "time_series", 1220 | "select": [ 1221 | [ 1222 | { 1223 | "params": [ 1224 | "battery_level" 1225 | ], 1226 | "type": "field" 1227 | }, 1228 | { 1229 | "params": [], 1230 | "type": "mean" 1231 | } 1232 | ] 1233 | ], 1234 | "tags": [ 1235 | { 1236 | "key": "metric", 1237 | "operator": "=", 1238 | "value": "battery_level" 1239 | }, 1240 | { 1241 | "condition": "AND", 1242 | "key": "display_name", 1243 | "operator": "=~", 1244 | "value": "/^$vehicle_name$/" 1245 | } 1246 | ] 1247 | }, 1248 | { 1249 | "alias": "charge_limit_soc", 1250 | "groupBy": [ 1251 | { 1252 | "params": [ 1253 | "$__interval" 1254 | ], 1255 | "type": "time" 1256 | }, 1257 | { 1258 | "params": [ 1259 | "previous" 1260 | ], 1261 | "type": "fill" 1262 | } 1263 | ], 1264 | "measurement": "charge_state", 1265 | "orderByTime": "ASC", 1266 | "policy": "default", 1267 | "query": "SELECT mean(\"charge_limit_soc\") FROM \"charge_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND $timeFilter GROUP BY time($__interval) fill(previous)", 1268 | "rawQuery": true, 1269 | "refId": "B", 1270 | "resultFormat": "time_series", 1271 | "select": [ 1272 | [ 1273 | { 1274 | "params": [ 1275 | "charge_limit_soc" 1276 | ], 1277 | "type": "field" 1278 | }, 1279 | { 1280 | "params": [], 1281 | "type": "mean" 1282 | } 1283 | ] 1284 | ], 1285 | "tags": [ 1286 | { 1287 | "key": "metric", 1288 | "operator": "=", 1289 | "value": "charge_limit_soc" 1290 | }, 1291 | { 1292 | "condition": "AND", 1293 | "key": "display_name", 1294 | "operator": "=~", 1295 | "value": "/^$vehicle_name$/" 1296 | } 1297 | ] 1298 | } 1299 | ], 1300 | "thresholds": [], 1301 | "timeFrom": null, 1302 | "timeRegions": [], 1303 | "timeShift": null, 1304 | "title": "Battery Level", 1305 | "tooltip": { 1306 | "shared": true, 1307 | "sort": 0, 1308 | "value_type": "individual" 1309 | }, 1310 | "type": "graph", 1311 | "xaxis": { 1312 | "buckets": null, 1313 | "mode": "time", 1314 | "name": null, 1315 | "show": true, 1316 | "values": [] 1317 | }, 1318 | "yaxes": [ 1319 | { 1320 | "decimals": null, 1321 | "format": "percent", 1322 | "label": null, 1323 | "logBase": 1, 1324 | "max": "100", 1325 | "min": "0", 1326 | "show": true 1327 | }, 1328 | { 1329 | "format": "short", 1330 | "label": null, 1331 | "logBase": 1, 1332 | "max": null, 1333 | "min": null, 1334 | "show": true 1335 | } 1336 | ], 1337 | "yaxis": { 1338 | "align": false, 1339 | "alignLevel": null 1340 | } 1341 | }, 1342 | { 1343 | "cacheTimeout": null, 1344 | "colorBackground": false, 1345 | "colorValue": true, 1346 | "colors": [ 1347 | "#e24d42", 1348 | "#629e51", 1349 | "#3f6833" 1350 | ], 1351 | "datasource": "${DS_TESLA}", 1352 | "description": "Power supplied by Charger.", 1353 | "format": "kwatt", 1354 | "gauge": { 1355 | "maxValue": 120, 1356 | "minValue": 0, 1357 | "show": true, 1358 | "thresholdLabels": false, 1359 | "thresholdMarkers": true 1360 | }, 1361 | "gridPos": { 1362 | "h": 5, 1363 | "w": 4, 1364 | "x": 0, 1365 | "y": 12 1366 | }, 1367 | "id": 8, 1368 | "interval": null, 1369 | "links": [], 1370 | "mappingType": 1, 1371 | "mappingTypes": [ 1372 | { 1373 | "name": "value to text", 1374 | "value": 1 1375 | }, 1376 | { 1377 | "name": "range to text", 1378 | "value": 2 1379 | } 1380 | ], 1381 | "maxDataPoints": 100, 1382 | "nullPointMode": "connected", 1383 | "nullText": null, 1384 | "postfix": "", 1385 | "postfixFontSize": "50%", 1386 | "prefix": "", 1387 | "prefixFontSize": "50%", 1388 | "rangeMaps": [ 1389 | { 1390 | "from": "null", 1391 | "text": "N/A", 1392 | "to": "null" 1393 | } 1394 | ], 1395 | "sparkline": { 1396 | "fillColor": "rgba(31, 118, 189, 0.18)", 1397 | "full": false, 1398 | "lineColor": "rgb(31, 120, 193)", 1399 | "show": false 1400 | }, 1401 | "tableColumn": "", 1402 | "targets": [ 1403 | { 1404 | "groupBy": [], 1405 | "measurement": "charge_state", 1406 | "orderByTime": "ASC", 1407 | "policy": "default", 1408 | "refId": "A", 1409 | "resultFormat": "time_series", 1410 | "select": [ 1411 | [ 1412 | { 1413 | "params": [ 1414 | "charger_power" 1415 | ], 1416 | "type": "field" 1417 | }, 1418 | { 1419 | "params": [], 1420 | "type": "last" 1421 | } 1422 | ] 1423 | ], 1424 | "tags": [ 1425 | { 1426 | "key": "display_name", 1427 | "operator": "=~", 1428 | "value": "/^$vehicle_name$/" 1429 | } 1430 | ] 1431 | } 1432 | ], 1433 | "thresholds": "10,10", 1434 | "title": "Charger Power", 1435 | "type": "singlestat", 1436 | "valueFontSize": "80%", 1437 | "valueMaps": [ 1438 | { 1439 | "op": "=", 1440 | "text": "N/A", 1441 | "value": "null" 1442 | } 1443 | ], 1444 | "valueName": "avg" 1445 | }, 1446 | { 1447 | "aliasColors": {}, 1448 | "bars": false, 1449 | "dashLength": 10, 1450 | "dashes": false, 1451 | "datasource": "${DS_TESLA}", 1452 | "fill": 1, 1453 | "gridPos": { 1454 | "h": 5, 1455 | "w": 20, 1456 | "x": 4, 1457 | "y": 12 1458 | }, 1459 | "id": 26, 1460 | "legend": { 1461 | "avg": false, 1462 | "current": false, 1463 | "max": false, 1464 | "min": false, 1465 | "show": true, 1466 | "total": false, 1467 | "values": false 1468 | }, 1469 | "lines": true, 1470 | "linewidth": 1, 1471 | "links": [], 1472 | "nullPointMode": "null", 1473 | "percentage": false, 1474 | "pointradius": 5, 1475 | "points": false, 1476 | "renderer": "flot", 1477 | "seriesOverrides": [], 1478 | "spaceLength": 10, 1479 | "stack": false, 1480 | "steppedLine": false, 1481 | "targets": [ 1482 | { 1483 | "alias": "charger_power", 1484 | "groupBy": [ 1485 | { 1486 | "params": [ 1487 | "$__interval" 1488 | ], 1489 | "type": "time" 1490 | }, 1491 | { 1492 | "params": [ 1493 | "previous" 1494 | ], 1495 | "type": "fill" 1496 | } 1497 | ], 1498 | "measurement": "charge_state", 1499 | "orderByTime": "ASC", 1500 | "policy": "default", 1501 | "refId": "A", 1502 | "resultFormat": "time_series", 1503 | "select": [ 1504 | [ 1505 | { 1506 | "params": [ 1507 | "charger_power" 1508 | ], 1509 | "type": "field" 1510 | }, 1511 | { 1512 | "params": [], 1513 | "type": "mean" 1514 | } 1515 | ] 1516 | ], 1517 | "tags": [ 1518 | { 1519 | "key": "display_name", 1520 | "operator": "=~", 1521 | "value": "/^$vehicle_name$/" 1522 | } 1523 | ] 1524 | }, 1525 | { 1526 | "alias": "power", 1527 | "groupBy": [ 1528 | { 1529 | "params": [ 1530 | "$__interval" 1531 | ], 1532 | "type": "time" 1533 | }, 1534 | { 1535 | "params": [ 1536 | "previous" 1537 | ], 1538 | "type": "fill" 1539 | } 1540 | ], 1541 | "measurement": "drive_state", 1542 | "orderByTime": "ASC", 1543 | "policy": "default", 1544 | "refId": "B", 1545 | "resultFormat": "time_series", 1546 | "select": [ 1547 | [ 1548 | { 1549 | "params": [ 1550 | "power" 1551 | ], 1552 | "type": "field" 1553 | }, 1554 | { 1555 | "params": [], 1556 | "type": "mean" 1557 | } 1558 | ] 1559 | ], 1560 | "tags": [ 1561 | { 1562 | "key": "display_name", 1563 | "operator": "=~", 1564 | "value": "/^$vehicle_name$/" 1565 | } 1566 | ] 1567 | } 1568 | ], 1569 | "thresholds": [], 1570 | "timeFrom": null, 1571 | "timeRegions": [], 1572 | "timeShift": null, 1573 | "title": "Chargerpower vs Batterypower", 1574 | "tooltip": { 1575 | "shared": true, 1576 | "sort": 0, 1577 | "value_type": "individual" 1578 | }, 1579 | "type": "graph", 1580 | "xaxis": { 1581 | "buckets": null, 1582 | "mode": "time", 1583 | "name": null, 1584 | "show": true, 1585 | "values": [] 1586 | }, 1587 | "yaxes": [ 1588 | { 1589 | "format": "kwatt", 1590 | "label": null, 1591 | "logBase": 1, 1592 | "max": null, 1593 | "min": null, 1594 | "show": true 1595 | }, 1596 | { 1597 | "format": "short", 1598 | "label": null, 1599 | "logBase": 1, 1600 | "max": null, 1601 | "min": null, 1602 | "show": true 1603 | } 1604 | ], 1605 | "yaxis": { 1606 | "align": false, 1607 | "alignLevel": null 1608 | } 1609 | }, 1610 | { 1611 | "backgroundColor": "rgba(128,128,128,0.1)", 1612 | "colorMaps": [ 1613 | { 1614 | "color": "#7EB26D", 1615 | "text": "Disconnected" 1616 | }, 1617 | { 1618 | "color": "#EAB839", 1619 | "text": "Starting" 1620 | }, 1621 | { 1622 | "color": "#bf1b00", 1623 | "text": "Charging" 1624 | }, 1625 | { 1626 | "color": "#EF843C", 1627 | "text": "Stopped" 1628 | }, 1629 | { 1630 | "color": "#7eb26d", 1631 | "text": "Complete" 1632 | }, 1633 | { 1634 | "color": "#1F78C1", 1635 | "text": "NoPower" 1636 | } 1637 | ], 1638 | "crosshairColor": "#8F070C", 1639 | "datasource": "${DS_TESLA}", 1640 | "display": "timeline", 1641 | "expandFromQueryS": 0, 1642 | "extendLastValue": true, 1643 | "gridPos": { 1644 | "h": 4, 1645 | "w": 20, 1646 | "x": 4, 1647 | "y": 17 1648 | }, 1649 | "highlightOnMouseover": true, 1650 | "id": 28, 1651 | "legendSortBy": "-ms", 1652 | "lineColor": "rgba(0,0,0,0.1)", 1653 | "links": [], 1654 | "metricNameColor": "#000000", 1655 | "rangeMaps": [ 1656 | { 1657 | "from": "null", 1658 | "text": "N/A", 1659 | "to": "null" 1660 | } 1661 | ], 1662 | "rowHeight": 50, 1663 | "showDistinctCount": false, 1664 | "showLegend": true, 1665 | "showLegendNames": false, 1666 | "showLegendPercent": true, 1667 | "showLegendValues": true, 1668 | "showTimeAxis": true, 1669 | "showTransitionCount": false, 1670 | "targets": [ 1671 | { 1672 | "groupBy": [], 1673 | "measurement": "charge_state", 1674 | "orderByTime": "ASC", 1675 | "policy": "default", 1676 | "query": "SELECT \"charging_state\" FROM \"charge_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND $timeFilter-5d", 1677 | "rawQuery": true, 1678 | "refId": "A", 1679 | "resultFormat": "time_series", 1680 | "select": [ 1681 | [ 1682 | { 1683 | "params": [ 1684 | "charging_state" 1685 | ], 1686 | "type": "field" 1687 | } 1688 | ] 1689 | ], 1690 | "tags": [ 1691 | { 1692 | "key": "metric", 1693 | "operator": "=", 1694 | "value": "charging_state" 1695 | }, 1696 | { 1697 | "condition": "AND", 1698 | "key": "display_name", 1699 | "operator": "=~", 1700 | "value": "/^$vehicle_name$/" 1701 | } 1702 | ] 1703 | } 1704 | ], 1705 | "textSize": 12, 1706 | "textSizeTime": 12, 1707 | "timeOptions": [ 1708 | { 1709 | "name": "Years", 1710 | "value": "years" 1711 | }, 1712 | { 1713 | "name": "Months", 1714 | "value": "months" 1715 | }, 1716 | { 1717 | "name": "Weeks", 1718 | "value": "weeks" 1719 | }, 1720 | { 1721 | "name": "Days", 1722 | "value": "days" 1723 | }, 1724 | { 1725 | "name": "Hours", 1726 | "value": "hours" 1727 | }, 1728 | { 1729 | "name": "Minutes", 1730 | "value": "minutes" 1731 | }, 1732 | { 1733 | "name": "Seconds", 1734 | "value": "seconds" 1735 | }, 1736 | { 1737 | "name": "Milliseconds", 1738 | "value": "milliseconds" 1739 | } 1740 | ], 1741 | "timePrecision": { 1742 | "name": "Minutes", 1743 | "value": "minutes" 1744 | }, 1745 | "timeTextColor": "#d8d9da", 1746 | "title": "Charger Events", 1747 | "type": "natel-discrete-panel", 1748 | "units": "short", 1749 | "useTimePrecision": false, 1750 | "valueMaps": [ 1751 | { 1752 | "op": "=", 1753 | "text": "N/A", 1754 | "value": "null" 1755 | } 1756 | ], 1757 | "valueTextColor": "#000000", 1758 | "writeAllValues": true, 1759 | "writeLastValue": true, 1760 | "writeMetricNames": false 1761 | }, 1762 | { 1763 | "backgroundColor": "rgba(128,128,128,0.1)", 1764 | "colorMaps": [ 1765 | { 1766 | "color": "#CCC", 1767 | "text": "N/A" 1768 | }, 1769 | { 1770 | "color": "#7EB26D", 1771 | "text": "false" 1772 | }, 1773 | { 1774 | "color": "#bf1b00", 1775 | "text": "true" 1776 | } 1777 | ], 1778 | "crosshairColor": "#8F070C", 1779 | "datasource": "${DS_TESLA}", 1780 | "display": "timeline", 1781 | "expandFromQueryS": 0, 1782 | "extendLastValue": true, 1783 | "gridPos": { 1784 | "h": 4, 1785 | "w": 20, 1786 | "x": 4, 1787 | "y": 21 1788 | }, 1789 | "hideTimeOverride": false, 1790 | "highlightOnMouseover": true, 1791 | "id": 34, 1792 | "legendSortBy": "-ms", 1793 | "lineColor": "rgba(0,0,0,0.1)", 1794 | "links": [], 1795 | "metricNameColor": "#000000", 1796 | "rangeMaps": [ 1797 | { 1798 | "from": "null", 1799 | "text": "N/A", 1800 | "to": "null" 1801 | } 1802 | ], 1803 | "rowHeight": 50, 1804 | "showLegend": true, 1805 | "showLegendNames": true, 1806 | "showLegendPercent": true, 1807 | "showLegendValues": true, 1808 | "showTimeAxis": true, 1809 | "targets": [ 1810 | { 1811 | "alias": "Battery Heater", 1812 | "groupBy": [], 1813 | "measurement": "charge_state", 1814 | "orderByTime": "ASC", 1815 | "policy": "default", 1816 | "query": "SELECT \"battery_heater_on\" FROM \"charge_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND $timeFilter-5d", 1817 | "rawQuery": true, 1818 | "refId": "A", 1819 | "resultFormat": "time_series", 1820 | "select": [ 1821 | [ 1822 | { 1823 | "params": [ 1824 | "battery_heater_on" 1825 | ], 1826 | "type": "field" 1827 | } 1828 | ] 1829 | ], 1830 | "tags": [ 1831 | { 1832 | "key": "metric", 1833 | "operator": "=", 1834 | "value": "battery_heater_on" 1835 | }, 1836 | { 1837 | "condition": "AND", 1838 | "key": "display_name", 1839 | "operator": "=~", 1840 | "value": "/^$vehicle_name$/" 1841 | } 1842 | ] 1843 | } 1844 | ], 1845 | "textSize": 24, 1846 | "textSizeTime": 12, 1847 | "timeOptions": [ 1848 | { 1849 | "name": "Years", 1850 | "value": "years" 1851 | }, 1852 | { 1853 | "name": "Months", 1854 | "value": "months" 1855 | }, 1856 | { 1857 | "name": "Weeks", 1858 | "value": "weeks" 1859 | }, 1860 | { 1861 | "name": "Days", 1862 | "value": "days" 1863 | }, 1864 | { 1865 | "name": "Hours", 1866 | "value": "hours" 1867 | }, 1868 | { 1869 | "name": "Minutes", 1870 | "value": "minutes" 1871 | }, 1872 | { 1873 | "name": "Seconds", 1874 | "value": "seconds" 1875 | }, 1876 | { 1877 | "name": "Milliseconds", 1878 | "value": "milliseconds" 1879 | } 1880 | ], 1881 | "timePrecision": { 1882 | "name": "Minutes", 1883 | "value": "minutes" 1884 | }, 1885 | "timeShift": null, 1886 | "timeTextColor": "#d8d9da", 1887 | "title": "Battery Heater", 1888 | "type": "natel-discrete-panel", 1889 | "units": "short", 1890 | "useTimePrecision": false, 1891 | "valueMaps": [ 1892 | { 1893 | "op": "=", 1894 | "text": "N/A", 1895 | "value": "null" 1896 | } 1897 | ], 1898 | "valueTextColor": "#000000", 1899 | "writeAllValues": false, 1900 | "writeLastValue": true, 1901 | "writeMetricNames": false 1902 | } 1903 | ], 1904 | "refresh": false, 1905 | "schemaVersion": 16, 1906 | "style": "dark", 1907 | "tags": [ 1908 | "tesla" 1909 | ], 1910 | "templating": { 1911 | "list": [ 1912 | { 1913 | "allValue": null, 1914 | "current": {}, 1915 | "datasource": "${DS_TESLA}", 1916 | "definition": "select distinct(display_name) from (select * from charge_state)", 1917 | "hide": 0, 1918 | "includeAll": false, 1919 | "label": null, 1920 | "multi": false, 1921 | "name": "vehicle_name", 1922 | "options": [], 1923 | "query": "select distinct(display_name) from (select * from charge_state)", 1924 | "refresh": 1, 1925 | "regex": "", 1926 | "skipUrlSync": false, 1927 | "sort": 0, 1928 | "tagValuesQuery": "", 1929 | "tags": [], 1930 | "tagsQuery": "", 1931 | "type": "query", 1932 | "useTags": false 1933 | }, 1934 | { 1935 | "current": { 1936 | "value": "${VAR_RANGEUNIT}", 1937 | "text": "${VAR_RANGEUNIT}" 1938 | }, 1939 | "hide": 2, 1940 | "label": null, 1941 | "name": "rangeunit", 1942 | "options": [ 1943 | { 1944 | "value": "${VAR_RANGEUNIT}", 1945 | "text": "${VAR_RANGEUNIT}" 1946 | } 1947 | ], 1948 | "query": "${VAR_RANGEUNIT}", 1949 | "skipUrlSync": false, 1950 | "type": "constant" 1951 | }, 1952 | { 1953 | "allValue": null, 1954 | "current": { 1955 | "text": "1.60934", 1956 | "value": "1.60934" 1957 | }, 1958 | "hide": 2, 1959 | "includeAll": false, 1960 | "label": null, 1961 | "multi": false, 1962 | "name": "rangefactor", 1963 | "options": [ 1964 | { 1965 | "selected": true, 1966 | "text": "1.60934", 1967 | "value": "1.60934" 1968 | } 1969 | ], 1970 | "query": "1.60934", 1971 | "skipUrlSync": false, 1972 | "type": "custom" 1973 | } 1974 | ] 1975 | }, 1976 | "time": { 1977 | "from": "now-6h", 1978 | "to": "now" 1979 | }, 1980 | "timepicker": { 1981 | "refresh_intervals": [ 1982 | "5s", 1983 | "10s", 1984 | "30s", 1985 | "1m", 1986 | "5m", 1987 | "15m", 1988 | "30m", 1989 | "1h", 1990 | "2h", 1991 | "1d" 1992 | ], 1993 | "time_options": [ 1994 | "5m", 1995 | "15m", 1996 | "1h", 1997 | "6h", 1998 | "12h", 1999 | "24h", 2000 | "2d", 2001 | "7d", 2002 | "30d" 2003 | ] 2004 | }, 2005 | "timezone": "", 2006 | "title": "Charging", 2007 | "uid": "dNviSBUmk", 2008 | "version": 141 2009 | } 2010 | -------------------------------------------------------------------------------- /grafana-dashboards/Driving.json: -------------------------------------------------------------------------------- 1 | { 2 | "__inputs": [ 3 | { 4 | "name": "DS_TESLA", 5 | "label": "tesla", 6 | "description": "", 7 | "type": "datasource", 8 | "pluginId": "influxdb", 9 | "pluginName": "InfluxDB" 10 | }, 11 | { 12 | "name": "VAR_RANGEUNIT", 13 | "type": "constant", 14 | "label": "rangeunit", 15 | "value": "km", 16 | "description": "" 17 | } 18 | ], 19 | "__requires": [ 20 | { 21 | "type": "grafana", 22 | "id": "grafana", 23 | "name": "Grafana", 24 | "version": "5.4.2" 25 | }, 26 | { 27 | "type": "panel", 28 | "id": "graph", 29 | "name": "Graph", 30 | "version": "5.0.0" 31 | }, 32 | { 33 | "type": "datasource", 34 | "id": "influxdb", 35 | "name": "InfluxDB", 36 | "version": "5.0.0" 37 | }, 38 | { 39 | "type": "panel", 40 | "id": "natel-discrete-panel", 41 | "name": "Discrete", 42 | "version": "0.0.9" 43 | }, 44 | { 45 | "type": "panel", 46 | "id": "pr0ps-trackmap-panel", 47 | "name": "TrackMap", 48 | "version": "2.0.0" 49 | }, 50 | { 51 | "type": "panel", 52 | "id": "singlestat", 53 | "name": "Singlestat", 54 | "version": "5.0.0" 55 | } 56 | ], 57 | "annotations": { 58 | "list": [ 59 | { 60 | "builtIn": 1, 61 | "datasource": "-- Grafana --", 62 | "enable": true, 63 | "hide": true, 64 | "iconColor": "rgba(0, 211, 255, 1)", 65 | "name": "Annotations & Alerts", 66 | "type": "dashboard" 67 | } 68 | ] 69 | }, 70 | "editable": true, 71 | "gnetId": null, 72 | "graphTooltip": 2, 73 | "id": null, 74 | "iteration": 1548415801098, 75 | "links": [], 76 | "panels": [ 77 | { 78 | "cacheTimeout": null, 79 | "colorBackground": false, 80 | "colorValue": false, 81 | "colors": [ 82 | "#299c46", 83 | "rgba(237, 129, 40, 0.89)", 84 | "#d44a3a" 85 | ], 86 | "datasource": "${DS_TESLA}", 87 | "format": "none", 88 | "gauge": { 89 | "maxValue": 100, 90 | "minValue": 0, 91 | "show": false, 92 | "thresholdLabels": false, 93 | "thresholdMarkers": true 94 | }, 95 | "gridPos": { 96 | "h": 3, 97 | "w": 2, 98 | "x": 0, 99 | "y": 0 100 | }, 101 | "id": 17, 102 | "interval": null, 103 | "links": [], 104 | "mappingType": 1, 105 | "mappingTypes": [ 106 | { 107 | "name": "value to text", 108 | "value": 1 109 | }, 110 | { 111 | "name": "range to text", 112 | "value": 2 113 | } 114 | ], 115 | "maxDataPoints": 100, 116 | "nullPointMode": "connected", 117 | "nullText": null, 118 | "postfix": "$rangeunit", 119 | "postfixFontSize": "30%", 120 | "prefix": "", 121 | "prefixFontSize": "50%", 122 | "rangeMaps": [ 123 | { 124 | "from": "null", 125 | "text": "N/A", 126 | "to": "null" 127 | } 128 | ], 129 | "sparkline": { 130 | "fillColor": "rgba(31, 118, 189, 0.18)", 131 | "full": false, 132 | "lineColor": "rgb(31, 120, 193)", 133 | "show": false 134 | }, 135 | "tableColumn": "", 136 | "targets": [ 137 | { 138 | "groupBy": [], 139 | "measurement": "vehicle_state", 140 | "orderByTime": "ASC", 141 | "policy": "default", 142 | "query": "SELECT last(\"odometer\") * $rangefactor FROM \"vehicle_state\" WHERE (\"metric\" = 'odometer') ", 143 | "rawQuery": false, 144 | "refId": "A", 145 | "resultFormat": "time_series", 146 | "select": [ 147 | [ 148 | { 149 | "params": [ 150 | "odometer" 151 | ], 152 | "type": "field" 153 | }, 154 | { 155 | "params": [], 156 | "type": "last" 157 | }, 158 | { 159 | "params": [ 160 | "*$rangefactor" 161 | ], 162 | "type": "math" 163 | } 164 | ] 165 | ], 166 | "tags": [ 167 | { 168 | "key": "display_name", 169 | "operator": "=~", 170 | "value": "/^$vehicle_name$/" 171 | } 172 | ] 173 | } 174 | ], 175 | "thresholds": "", 176 | "title": "Odometer", 177 | "type": "singlestat", 178 | "valueFontSize": "80%", 179 | "valueMaps": [ 180 | { 181 | "op": "=", 182 | "text": "N/A", 183 | "value": "null" 184 | } 185 | ], 186 | "valueName": "avg" 187 | }, 188 | { 189 | "cacheTimeout": null, 190 | "colorBackground": false, 191 | "colorValue": false, 192 | "colors": [ 193 | "#299c46", 194 | "rgba(237, 129, 40, 0.89)", 195 | "#d44a3a" 196 | ], 197 | "datasource": "${DS_TESLA}", 198 | "format": "none", 199 | "gauge": { 200 | "maxValue": 100, 201 | "minValue": 0, 202 | "show": false, 203 | "thresholdLabels": false, 204 | "thresholdMarkers": true 205 | }, 206 | "gridPos": { 207 | "h": 3, 208 | "w": 2, 209 | "x": 2, 210 | "y": 0 211 | }, 212 | "id": 28, 213 | "interval": null, 214 | "links": [], 215 | "mappingType": 1, 216 | "mappingTypes": [ 217 | { 218 | "name": "value to text", 219 | "value": 1 220 | }, 221 | { 222 | "name": "range to text", 223 | "value": 2 224 | } 225 | ], 226 | "maxDataPoints": 100, 227 | "nullPointMode": "connected", 228 | "nullText": null, 229 | "postfix": "$rangeunit", 230 | "postfixFontSize": "30%", 231 | "prefix": "", 232 | "prefixFontSize": "50%", 233 | "rangeMaps": [ 234 | { 235 | "from": "null", 236 | "text": "N/A", 237 | "to": "null" 238 | } 239 | ], 240 | "sparkline": { 241 | "fillColor": "rgba(31, 118, 189, 0.18)", 242 | "full": false, 243 | "lineColor": "rgb(31, 120, 193)", 244 | "show": false 245 | }, 246 | "tableColumn": "", 247 | "targets": [ 248 | { 249 | "alias": "Distance driven", 250 | "groupBy": [], 251 | "measurement": "vehicle_state", 252 | "orderByTime": "ASC", 253 | "policy": "default", 254 | "refId": "A", 255 | "resultFormat": "time_series", 256 | "select": [ 257 | [ 258 | { 259 | "params": [ 260 | "odometer" 261 | ], 262 | "type": "field" 263 | }, 264 | { 265 | "params": [], 266 | "type": "spread" 267 | }, 268 | { 269 | "params": [ 270 | "* $rangefactor" 271 | ], 272 | "type": "math" 273 | } 274 | ] 275 | ], 276 | "tags": [ 277 | { 278 | "key": "display_name", 279 | "operator": "=~", 280 | "value": "/^$vehicle_name$/" 281 | } 282 | ] 283 | } 284 | ], 285 | "thresholds": "", 286 | "title": "Distance", 287 | "type": "singlestat", 288 | "valueFontSize": "80%", 289 | "valueMaps": [ 290 | { 291 | "op": "=", 292 | "text": "N/A", 293 | "value": "null" 294 | } 295 | ], 296 | "valueName": "avg" 297 | }, 298 | { 299 | "cacheTimeout": null, 300 | "colorBackground": false, 301 | "colorValue": false, 302 | "colors": [ 303 | "#299c46", 304 | "rgba(237, 129, 40, 0.89)", 305 | "#d44a3a" 306 | ], 307 | "datasource": "${DS_TESLA}", 308 | "decimals": 0, 309 | "format": "none", 310 | "gauge": { 311 | "maxValue": 100, 312 | "minValue": 0, 313 | "show": false, 314 | "thresholdLabels": false, 315 | "thresholdMarkers": true 316 | }, 317 | "gridPos": { 318 | "h": 3, 319 | "w": 2, 320 | "x": 4, 321 | "y": 0 322 | }, 323 | "id": 30, 324 | "interval": null, 325 | "links": [], 326 | "mappingType": 1, 327 | "mappingTypes": [ 328 | { 329 | "name": "value to text", 330 | "value": 1 331 | }, 332 | { 333 | "name": "range to text", 334 | "value": 2 335 | } 336 | ], 337 | "maxDataPoints": 100, 338 | "nullPointMode": "connected", 339 | "nullText": null, 340 | "postfix": "kwh", 341 | "postfixFontSize": "30%", 342 | "prefix": "", 343 | "prefixFontSize": "50%", 344 | "rangeMaps": [ 345 | { 346 | "from": "null", 347 | "text": "N/A", 348 | "to": "null" 349 | } 350 | ], 351 | "sparkline": { 352 | "fillColor": "rgba(31, 118, 189, 0.18)", 353 | "full": false, 354 | "lineColor": "rgb(31, 120, 193)", 355 | "show": false 356 | }, 357 | "tableColumn": "", 358 | "targets": [ 359 | { 360 | "groupBy": [ 361 | { 362 | "params": [ 363 | "1s" 364 | ], 365 | "type": "time" 366 | }, 367 | { 368 | "params": [ 369 | "0" 370 | ], 371 | "type": "fill" 372 | } 373 | ], 374 | "measurement": "drive_state", 375 | "orderByTime": "ASC", 376 | "policy": "default", 377 | "query": "select cumulative_sum(psum)/1000 from (SELECT sum(\"power\") as psum FROM \"drive_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND power > 0 AND $timeFilter GROUP BY time(3600s) fill(0))", 378 | "rawQuery": true, 379 | "refId": "A", 380 | "resultFormat": "time_series", 381 | "select": [ 382 | [ 383 | { 384 | "params": [ 385 | "power" 386 | ], 387 | "type": "field" 388 | }, 389 | { 390 | "params": [], 391 | "type": "sum" 392 | } 393 | ] 394 | ], 395 | "tags": [ 396 | { 397 | "key": "metric", 398 | "operator": "=", 399 | "value": "power" 400 | }, 401 | { 402 | "condition": "AND", 403 | "key": "display_name", 404 | "operator": "=~", 405 | "value": "/^$vehicle_name$/" 406 | } 407 | ] 408 | } 409 | ], 410 | "thresholds": "", 411 | "title": "Panel Title", 412 | "type": "singlestat", 413 | "valueFontSize": "80%", 414 | "valueMaps": [ 415 | { 416 | "op": "=", 417 | "text": "N/A", 418 | "value": "null" 419 | } 420 | ], 421 | "valueName": "avg" 422 | }, 423 | { 424 | "cacheTimeout": null, 425 | "colorBackground": false, 426 | "colorValue": false, 427 | "colors": [ 428 | "#299c46", 429 | "rgba(237, 129, 40, 0.89)", 430 | "#d44a3a" 431 | ], 432 | "datasource": "${DS_TESLA}", 433 | "format": "none", 434 | "gauge": { 435 | "maxValue": 100, 436 | "minValue": 0, 437 | "show": false, 438 | "thresholdLabels": false, 439 | "thresholdMarkers": true 440 | }, 441 | "gridPos": { 442 | "h": 3, 443 | "w": 2, 444 | "x": 6, 445 | "y": 0 446 | }, 447 | "id": 22, 448 | "interval": null, 449 | "links": [], 450 | "mappingType": 1, 451 | "mappingTypes": [ 452 | { 453 | "name": "value to text", 454 | "value": 1 455 | }, 456 | { 457 | "name": "range to text", 458 | "value": 2 459 | } 460 | ], 461 | "maxDataPoints": 100, 462 | "nullPointMode": "connected", 463 | "nullText": null, 464 | "postfix": "$rangeunit", 465 | "postfixFontSize": "30%", 466 | "prefix": "", 467 | "prefixFontSize": "50%", 468 | "rangeMaps": [ 469 | { 470 | "from": "null", 471 | "text": "N/A", 472 | "to": "null" 473 | } 474 | ], 475 | "sparkline": { 476 | "fillColor": "rgba(31, 118, 189, 0.18)", 477 | "full": false, 478 | "lineColor": "rgb(31, 120, 193)", 479 | "show": false 480 | }, 481 | "tableColumn": "", 482 | "targets": [ 483 | { 484 | "groupBy": [], 485 | "measurement": "charge_state", 486 | "orderByTime": "ASC", 487 | "policy": "default", 488 | "refId": "A", 489 | "resultFormat": "time_series", 490 | "select": [ 491 | [ 492 | { 493 | "params": [ 494 | "est_battery_range" 495 | ], 496 | "type": "field" 497 | }, 498 | { 499 | "params": [ 500 | "*$rangefactor" 501 | ], 502 | "type": "math" 503 | } 504 | ] 505 | ], 506 | "tags": [ 507 | { 508 | "key": "display_name", 509 | "operator": "=~", 510 | "value": "/^$vehicle_name$/" 511 | } 512 | ] 513 | } 514 | ], 515 | "thresholds": "", 516 | "title": "Range", 517 | "type": "singlestat", 518 | "valueFontSize": "80%", 519 | "valueMaps": [ 520 | { 521 | "op": "=", 522 | "text": "N/A", 523 | "value": "null" 524 | } 525 | ], 526 | "valueName": "current" 527 | }, 528 | { 529 | "cacheTimeout": null, 530 | "colorBackground": false, 531 | "colorValue": true, 532 | "colors": [ 533 | "#1f78c1", 534 | "#508642", 535 | "#ea6460" 536 | ], 537 | "datasource": "${DS_TESLA}", 538 | "decimals": 1, 539 | "format": "celsius", 540 | "gauge": { 541 | "maxValue": 50, 542 | "minValue": -15, 543 | "show": true, 544 | "thresholdLabels": false, 545 | "thresholdMarkers": true 546 | }, 547 | "gridPos": { 548 | "h": 3, 549 | "w": 3, 550 | "x": 8, 551 | "y": 0 552 | }, 553 | "id": 2, 554 | "interval": null, 555 | "links": [], 556 | "mappingType": 1, 557 | "mappingTypes": [ 558 | { 559 | "name": "value to text", 560 | "value": 1 561 | }, 562 | { 563 | "name": "range to text", 564 | "value": 2 565 | } 566 | ], 567 | "maxDataPoints": 100, 568 | "nullPointMode": "connected", 569 | "nullText": null, 570 | "postfix": "", 571 | "postfixFontSize": "50%", 572 | "prefix": "", 573 | "prefixFontSize": "50%", 574 | "rangeMaps": [ 575 | { 576 | "from": "null", 577 | "text": "N/A", 578 | "to": "null" 579 | } 580 | ], 581 | "sparkline": { 582 | "fillColor": "rgba(31, 118, 189, 0.18)", 583 | "full": false, 584 | "lineColor": "rgb(31, 120, 193)", 585 | "show": true 586 | }, 587 | "tableColumn": "", 588 | "targets": [ 589 | { 590 | "groupBy": [ 591 | { 592 | "params": [ 593 | "$__interval" 594 | ], 595 | "type": "time" 596 | }, 597 | { 598 | "params": [ 599 | "previous" 600 | ], 601 | "type": "fill" 602 | } 603 | ], 604 | "measurement": "climate_state", 605 | "orderByTime": "ASC", 606 | "policy": "default", 607 | "refId": "A", 608 | "resultFormat": "time_series", 609 | "select": [ 610 | [ 611 | { 612 | "params": [ 613 | "outside_temp" 614 | ], 615 | "type": "field" 616 | }, 617 | { 618 | "params": [], 619 | "type": "mean" 620 | } 621 | ] 622 | ], 623 | "tags": [ 624 | { 625 | "key": "display_name", 626 | "operator": "=~", 627 | "value": "/^$vehicle_name$/" 628 | } 629 | ] 630 | } 631 | ], 632 | "thresholds": "0,35", 633 | "title": "Outside Temperatur", 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 | "#1f78c1", 651 | "#508642", 652 | "#ea6460" 653 | ], 654 | "datasource": "${DS_TESLA}", 655 | "decimals": 1, 656 | "format": "celsius", 657 | "gauge": { 658 | "maxValue": 50, 659 | "minValue": -15, 660 | "show": true, 661 | "thresholdLabels": false, 662 | "thresholdMarkers": true 663 | }, 664 | "gridPos": { 665 | "h": 3, 666 | "w": 3, 667 | "x": 11, 668 | "y": 0 669 | }, 670 | "id": 18, 671 | "interval": null, 672 | "links": [], 673 | "mappingType": 1, 674 | "mappingTypes": [ 675 | { 676 | "name": "value to text", 677 | "value": 1 678 | }, 679 | { 680 | "name": "range to text", 681 | "value": 2 682 | } 683 | ], 684 | "maxDataPoints": 100, 685 | "nullPointMode": "connected", 686 | "nullText": null, 687 | "postfix": "", 688 | "postfixFontSize": "50%", 689 | "prefix": "", 690 | "prefixFontSize": "50%", 691 | "rangeMaps": [ 692 | { 693 | "from": "null", 694 | "text": "N/A", 695 | "to": "null" 696 | } 697 | ], 698 | "sparkline": { 699 | "fillColor": "rgba(31, 118, 189, 0.18)", 700 | "full": false, 701 | "lineColor": "rgb(31, 120, 193)", 702 | "show": true 703 | }, 704 | "tableColumn": "", 705 | "targets": [ 706 | { 707 | "groupBy": [ 708 | { 709 | "params": [ 710 | "$__interval" 711 | ], 712 | "type": "time" 713 | }, 714 | { 715 | "params": [ 716 | "previous" 717 | ], 718 | "type": "fill" 719 | } 720 | ], 721 | "measurement": "climate_state", 722 | "orderByTime": "ASC", 723 | "policy": "default", 724 | "refId": "A", 725 | "resultFormat": "time_series", 726 | "select": [ 727 | [ 728 | { 729 | "params": [ 730 | "inside_temp" 731 | ], 732 | "type": "field" 733 | }, 734 | { 735 | "params": [], 736 | "type": "mean" 737 | } 738 | ] 739 | ], 740 | "tags": [ 741 | { 742 | "key": "display_name", 743 | "operator": "=~", 744 | "value": "/^$vehicle_name$/" 745 | } 746 | ] 747 | } 748 | ], 749 | "thresholds": "0,35", 750 | "title": "Inside Temperatur", 751 | "type": "singlestat", 752 | "valueFontSize": "50%", 753 | "valueMaps": [ 754 | { 755 | "op": "=", 756 | "text": "N/A", 757 | "value": "null" 758 | } 759 | ], 760 | "valueName": "current" 761 | }, 762 | { 763 | "autoZoom": true, 764 | "datasource": "${DS_TESLA}", 765 | "gridPos": { 766 | "h": 13, 767 | "w": 10, 768 | "x": 14, 769 | "y": 0 770 | }, 771 | "id": 15, 772 | "lineColor": "red", 773 | "links": [], 774 | "maxDataPoints": 1000, 775 | "pointColor": "royalblue", 776 | "targets": [ 777 | { 778 | "groupBy": [ 779 | { 780 | "params": [ 781 | "10s" 782 | ], 783 | "type": "time" 784 | }, 785 | { 786 | "params": [ 787 | "previous" 788 | ], 789 | "type": "fill" 790 | } 791 | ], 792 | "measurement": "drive_state", 793 | "orderByTime": "ASC", 794 | "policy": "default", 795 | "query": "SELECT \"native_latitude\" AS \"latitude\", \"native_longitude\" AS \"longitude\" FROM \"drive_state\" WHERE (\"display_name\" = '/^$vehicle_name$/') AND time >= now() - 6h", 796 | "rawQuery": false, 797 | "refId": "A", 798 | "resultFormat": "time_series", 799 | "select": [ 800 | [ 801 | { 802 | "params": [ 803 | "native_latitude" 804 | ], 805 | "type": "field" 806 | }, 807 | { 808 | "params": [], 809 | "type": "median" 810 | } 811 | ] 812 | ], 813 | "tags": [ 814 | { 815 | "key": "display_name", 816 | "operator": "=~", 817 | "value": "/^$vehicle_name$/" 818 | } 819 | ] 820 | }, 821 | { 822 | "groupBy": [ 823 | { 824 | "params": [ 825 | "10s" 826 | ], 827 | "type": "time" 828 | }, 829 | { 830 | "params": [ 831 | "previous" 832 | ], 833 | "type": "fill" 834 | } 835 | ], 836 | "measurement": "drive_state", 837 | "orderByTime": "ASC", 838 | "policy": "default", 839 | "refId": "B", 840 | "resultFormat": "time_series", 841 | "select": [ 842 | [ 843 | { 844 | "params": [ 845 | "native_longitude" 846 | ], 847 | "type": "field" 848 | }, 849 | { 850 | "params": [], 851 | "type": "median" 852 | } 853 | ] 854 | ], 855 | "tags": [ 856 | { 857 | "key": "display_name", 858 | "operator": "=~", 859 | "value": "/^$vehicle_name$/" 860 | } 861 | ] 862 | } 863 | ], 864 | "title": "Geolocation", 865 | "transparent": true, 866 | "type": "pr0ps-trackmap-panel" 867 | }, 868 | { 869 | "cacheTimeout": null, 870 | "colorBackground": false, 871 | "colorValue": false, 872 | "colors": [ 873 | "#299c46", 874 | "rgba(237, 129, 40, 0.89)", 875 | "#d44a3a" 876 | ], 877 | "datasource": "${DS_TESLA}", 878 | "format": "none", 879 | "gauge": { 880 | "maxValue": 100, 881 | "minValue": 0, 882 | "show": true, 883 | "thresholdLabels": false, 884 | "thresholdMarkers": true 885 | }, 886 | "gridPos": { 887 | "h": 5, 888 | "w": 4, 889 | "x": 0, 890 | "y": 3 891 | }, 892 | "id": 10, 893 | "interval": null, 894 | "links": [], 895 | "mappingType": 1, 896 | "mappingTypes": [ 897 | { 898 | "name": "value to text", 899 | "value": 1 900 | }, 901 | { 902 | "name": "range to text", 903 | "value": 2 904 | } 905 | ], 906 | "maxDataPoints": 100, 907 | "nullPointMode": "connected", 908 | "nullText": null, 909 | "postfix": " $rangeunit/h", 910 | "postfixFontSize": "50%", 911 | "prefix": "", 912 | "prefixFontSize": "50%", 913 | "rangeMaps": [ 914 | { 915 | "from": "null", 916 | "text": "N/A", 917 | "to": "null" 918 | } 919 | ], 920 | "sparkline": { 921 | "fillColor": "rgba(31, 118, 189, 0.18)", 922 | "full": false, 923 | "lineColor": "rgb(31, 120, 193)", 924 | "show": false 925 | }, 926 | "tableColumn": "", 927 | "targets": [ 928 | { 929 | "groupBy": [], 930 | "measurement": "drive_state", 931 | "orderByTime": "ASC", 932 | "policy": "default", 933 | "query": "SELECT last(\"speed\") * $rangefactor FROM \"drive_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/)", 934 | "rawQuery": true, 935 | "refId": "A", 936 | "resultFormat": "time_series", 937 | "select": [ 938 | [ 939 | { 940 | "params": [ 941 | "speed" 942 | ], 943 | "type": "field" 944 | }, 945 | { 946 | "params": [], 947 | "type": "last" 948 | }, 949 | { 950 | "params": [ 951 | "* 1.60934" 952 | ], 953 | "type": "math" 954 | } 955 | ] 956 | ], 957 | "tags": [ 958 | { 959 | "key": "metric", 960 | "operator": "=", 961 | "value": "speed" 962 | }, 963 | { 964 | "condition": "AND", 965 | "key": "display_name", 966 | "operator": "=~", 967 | "value": "/^$vehicle_name$/" 968 | } 969 | ] 970 | } 971 | ], 972 | "thresholds": "10,80,150", 973 | "title": "Vehicle Speed", 974 | "type": "singlestat", 975 | "valueFontSize": "80%", 976 | "valueMaps": [ 977 | { 978 | "op": "=", 979 | "text": "N/A", 980 | "value": "null" 981 | } 982 | ], 983 | "valueName": "avg" 984 | }, 985 | { 986 | "aliasColors": {}, 987 | "bars": false, 988 | "dashLength": 10, 989 | "dashes": false, 990 | "datasource": "${DS_TESLA}", 991 | "fill": 1, 992 | "gridPos": { 993 | "h": 5, 994 | "w": 10, 995 | "x": 4, 996 | "y": 3 997 | }, 998 | "id": 8, 999 | "legend": { 1000 | "avg": false, 1001 | "current": false, 1002 | "max": false, 1003 | "min": false, 1004 | "show": true, 1005 | "total": false, 1006 | "values": false 1007 | }, 1008 | "lines": true, 1009 | "linewidth": 1, 1010 | "links": [], 1011 | "nullPointMode": "connected", 1012 | "percentage": false, 1013 | "pointradius": 5, 1014 | "points": false, 1015 | "renderer": "flot", 1016 | "seriesOverrides": [ 1017 | { 1018 | "alias": "Battery Level", 1019 | "fill": 0, 1020 | "yaxis": 2 1021 | } 1022 | ], 1023 | "spaceLength": 10, 1024 | "stack": false, 1025 | "steppedLine": false, 1026 | "targets": [ 1027 | { 1028 | "alias": "speed", 1029 | "groupBy": [ 1030 | { 1031 | "params": [ 1032 | "1s" 1033 | ], 1034 | "type": "time" 1035 | }, 1036 | { 1037 | "params": [ 1038 | "previous" 1039 | ], 1040 | "type": "fill" 1041 | } 1042 | ], 1043 | "measurement": "drive_state", 1044 | "orderByTime": "ASC", 1045 | "policy": "default", 1046 | "query": "SELECT mean(\"speed\") * $rangefactor FROM \"drive_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/ ) AND $timeFilter GROUP BY time($__interval) fill(previous)", 1047 | "rawQuery": true, 1048 | "refId": "A", 1049 | "resultFormat": "time_series", 1050 | "select": [ 1051 | [ 1052 | { 1053 | "params": [ 1054 | "speed" 1055 | ], 1056 | "type": "field" 1057 | }, 1058 | { 1059 | "params": [], 1060 | "type": "mean" 1061 | }, 1062 | { 1063 | "params": [ 1064 | "* 1.60934" 1065 | ], 1066 | "type": "math" 1067 | } 1068 | ] 1069 | ], 1070 | "tags": [ 1071 | { 1072 | "key": "display_name", 1073 | "operator": "=~", 1074 | "value": "/^$vehicle_name$/" 1075 | }, 1076 | { 1077 | "condition": "AND", 1078 | "key": "metric", 1079 | "operator": "=", 1080 | "value": "speed" 1081 | } 1082 | ] 1083 | }, 1084 | { 1085 | "alias": "Battery Level", 1086 | "groupBy": [ 1087 | { 1088 | "params": [ 1089 | "$__interval" 1090 | ], 1091 | "type": "time" 1092 | }, 1093 | { 1094 | "params": [ 1095 | "previous" 1096 | ], 1097 | "type": "fill" 1098 | } 1099 | ], 1100 | "measurement": "charge_state", 1101 | "orderByTime": "ASC", 1102 | "policy": "default", 1103 | "refId": "B", 1104 | "resultFormat": "time_series", 1105 | "select": [ 1106 | [ 1107 | { 1108 | "params": [ 1109 | "battery_level" 1110 | ], 1111 | "type": "field" 1112 | }, 1113 | { 1114 | "params": [], 1115 | "type": "mean" 1116 | } 1117 | ] 1118 | ], 1119 | "tags": [ 1120 | { 1121 | "key": "metric", 1122 | "operator": "=", 1123 | "value": "battery_level" 1124 | }, 1125 | { 1126 | "condition": "AND", 1127 | "key": "display_name", 1128 | "operator": "=~", 1129 | "value": "/^$vehicle_name$/" 1130 | } 1131 | ] 1132 | } 1133 | ], 1134 | "thresholds": [], 1135 | "timeFrom": null, 1136 | "timeRegions": [], 1137 | "timeShift": null, 1138 | "title": "Vehicle Speed", 1139 | "tooltip": { 1140 | "shared": true, 1141 | "sort": 0, 1142 | "value_type": "individual" 1143 | }, 1144 | "type": "graph", 1145 | "xaxis": { 1146 | "buckets": null, 1147 | "mode": "time", 1148 | "name": null, 1149 | "show": true, 1150 | "values": [] 1151 | }, 1152 | "yaxes": [ 1153 | { 1154 | "format": "none", 1155 | "label": "Speed", 1156 | "logBase": 1, 1157 | "max": null, 1158 | "min": null, 1159 | "show": true 1160 | }, 1161 | { 1162 | "format": "percent", 1163 | "label": "Battery", 1164 | "logBase": 1, 1165 | "max": null, 1166 | "min": null, 1167 | "show": true 1168 | } 1169 | ], 1170 | "yaxis": { 1171 | "align": false, 1172 | "alignLevel": null 1173 | } 1174 | }, 1175 | { 1176 | "cacheTimeout": null, 1177 | "colorBackground": false, 1178 | "colorValue": false, 1179 | "colors": [ 1180 | "#299c46", 1181 | "rgba(237, 129, 40, 0.89)", 1182 | "#d44a3a" 1183 | ], 1184 | "datasource": "${DS_TESLA}", 1185 | "format": "kwatt", 1186 | "gauge": { 1187 | "maxValue": 300, 1188 | "minValue": 0, 1189 | "show": true, 1190 | "thresholdLabels": false, 1191 | "thresholdMarkers": true 1192 | }, 1193 | "gridPos": { 1194 | "h": 5, 1195 | "w": 4, 1196 | "x": 0, 1197 | "y": 8 1198 | }, 1199 | "id": 11, 1200 | "interval": null, 1201 | "links": [], 1202 | "mappingType": 1, 1203 | "mappingTypes": [ 1204 | { 1205 | "name": "value to text", 1206 | "value": 1 1207 | }, 1208 | { 1209 | "name": "range to text", 1210 | "value": 2 1211 | } 1212 | ], 1213 | "maxDataPoints": 100, 1214 | "nullPointMode": "connected", 1215 | "nullText": null, 1216 | "postfix": "", 1217 | "postfixFontSize": "50%", 1218 | "prefix": "", 1219 | "prefixFontSize": "50%", 1220 | "rangeMaps": [ 1221 | { 1222 | "from": "null", 1223 | "text": "N/A", 1224 | "to": "null" 1225 | } 1226 | ], 1227 | "sparkline": { 1228 | "fillColor": "rgba(31, 118, 189, 0.18)", 1229 | "full": false, 1230 | "lineColor": "rgb(31, 120, 193)", 1231 | "show": false 1232 | }, 1233 | "tableColumn": "", 1234 | "targets": [ 1235 | { 1236 | "groupBy": [], 1237 | "measurement": "drive_state", 1238 | "orderByTime": "ASC", 1239 | "policy": "default", 1240 | "query": "SELECT last(\"power\") FROM \"drive_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/)", 1241 | "rawQuery": true, 1242 | "refId": "A", 1243 | "resultFormat": "time_series", 1244 | "select": [ 1245 | [ 1246 | { 1247 | "params": [ 1248 | "power" 1249 | ], 1250 | "type": "field" 1251 | }, 1252 | { 1253 | "params": [], 1254 | "type": "last" 1255 | } 1256 | ] 1257 | ], 1258 | "tags": [ 1259 | { 1260 | "key": "metric", 1261 | "operator": "=", 1262 | "value": "power" 1263 | }, 1264 | { 1265 | "condition": "AND", 1266 | "key": "display_name", 1267 | "operator": "=~", 1268 | "value": "/^$vehicle_name$/" 1269 | } 1270 | ] 1271 | } 1272 | ], 1273 | "thresholds": "75,180,300", 1274 | "title": "Vehicle Power", 1275 | "type": "singlestat", 1276 | "valueFontSize": "80%", 1277 | "valueMaps": [ 1278 | { 1279 | "op": "=", 1280 | "text": "N/A", 1281 | "value": "null" 1282 | } 1283 | ], 1284 | "valueName": "avg" 1285 | }, 1286 | { 1287 | "aliasColors": {}, 1288 | "bars": false, 1289 | "dashLength": 10, 1290 | "dashes": false, 1291 | "datasource": "${DS_TESLA}", 1292 | "fill": 1, 1293 | "gridPos": { 1294 | "h": 5, 1295 | "w": 10, 1296 | "x": 4, 1297 | "y": 8 1298 | }, 1299 | "id": 6, 1300 | "legend": { 1301 | "avg": false, 1302 | "current": false, 1303 | "max": false, 1304 | "min": false, 1305 | "show": true, 1306 | "total": false, 1307 | "values": false 1308 | }, 1309 | "lines": true, 1310 | "linewidth": 1, 1311 | "links": [], 1312 | "nullPointMode": "connected", 1313 | "percentage": false, 1314 | "pointradius": 5, 1315 | "points": false, 1316 | "renderer": "flot", 1317 | "seriesOverrides": [ 1318 | { 1319 | "alias": "Elevation", 1320 | "yaxis": 2 1321 | } 1322 | ], 1323 | "spaceLength": 10, 1324 | "stack": false, 1325 | "steppedLine": false, 1326 | "targets": [ 1327 | { 1328 | "alias": "power", 1329 | "groupBy": [ 1330 | { 1331 | "params": [ 1332 | "1s" 1333 | ], 1334 | "type": "time" 1335 | }, 1336 | { 1337 | "params": [ 1338 | "previous" 1339 | ], 1340 | "type": "fill" 1341 | } 1342 | ], 1343 | "measurement": "drive_state", 1344 | "orderByTime": "ASC", 1345 | "policy": "default", 1346 | "query": "SELECT mean(\"power\") FROM \"drive_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND $timeFilter GROUP BY time($__interval) fill(previous)", 1347 | "rawQuery": true, 1348 | "refId": "A", 1349 | "resultFormat": "time_series", 1350 | "select": [ 1351 | [ 1352 | { 1353 | "params": [ 1354 | "power" 1355 | ], 1356 | "type": "field" 1357 | }, 1358 | { 1359 | "params": [], 1360 | "type": "mean" 1361 | } 1362 | ] 1363 | ], 1364 | "tags": [ 1365 | { 1366 | "key": "metric", 1367 | "operator": "=", 1368 | "value": "power" 1369 | }, 1370 | { 1371 | "condition": "AND", 1372 | "key": "display_name", 1373 | "operator": "=~", 1374 | "value": "/^$vehicle_name$/" 1375 | } 1376 | ] 1377 | }, 1378 | { 1379 | "alias": "Elevation", 1380 | "groupBy": [ 1381 | { 1382 | "params": [ 1383 | "$__interval" 1384 | ], 1385 | "type": "time" 1386 | }, 1387 | { 1388 | "params": [ 1389 | "null" 1390 | ], 1391 | "type": "fill" 1392 | } 1393 | ], 1394 | "measurement": "drive_state", 1395 | "orderByTime": "ASC", 1396 | "policy": "default", 1397 | "refId": "B", 1398 | "resultFormat": "time_series", 1399 | "select": [ 1400 | [ 1401 | { 1402 | "params": [ 1403 | "elevation" 1404 | ], 1405 | "type": "field" 1406 | }, 1407 | { 1408 | "params": [], 1409 | "type": "mean" 1410 | } 1411 | ] 1412 | ], 1413 | "tags": [ 1414 | { 1415 | "key": "display_name", 1416 | "operator": "=~", 1417 | "value": "/^$vehicle_name$/" 1418 | } 1419 | ] 1420 | } 1421 | ], 1422 | "thresholds": [], 1423 | "timeFrom": null, 1424 | "timeRegions": [], 1425 | "timeShift": null, 1426 | "title": "Power Revenue", 1427 | "tooltip": { 1428 | "shared": true, 1429 | "sort": 0, 1430 | "value_type": "individual" 1431 | }, 1432 | "type": "graph", 1433 | "xaxis": { 1434 | "buckets": null, 1435 | "mode": "time", 1436 | "name": null, 1437 | "show": true, 1438 | "values": [] 1439 | }, 1440 | "yaxes": [ 1441 | { 1442 | "format": "kwatt", 1443 | "label": null, 1444 | "logBase": 1, 1445 | "max": null, 1446 | "min": null, 1447 | "show": true 1448 | }, 1449 | { 1450 | "format": "lengthm", 1451 | "label": null, 1452 | "logBase": 2, 1453 | "max": null, 1454 | "min": "0", 1455 | "show": true 1456 | } 1457 | ], 1458 | "yaxis": { 1459 | "align": true, 1460 | "alignLevel": null 1461 | } 1462 | }, 1463 | { 1464 | "cacheTimeout": null, 1465 | "colorBackground": false, 1466 | "colorValue": false, 1467 | "colors": [ 1468 | "#299c46", 1469 | "rgba(237, 129, 40, 0.89)", 1470 | "#d44a3a" 1471 | ], 1472 | "datasource": "${DS_TESLA}", 1473 | "format": "none", 1474 | "gauge": { 1475 | "maxValue": 100, 1476 | "minValue": 0, 1477 | "show": false, 1478 | "thresholdLabels": false, 1479 | "thresholdMarkers": true 1480 | }, 1481 | "gridPos": { 1482 | "h": 3, 1483 | "w": 2, 1484 | "x": 0, 1485 | "y": 13 1486 | }, 1487 | "id": 13, 1488 | "interval": null, 1489 | "links": [], 1490 | "mappingType": 1, 1491 | "mappingTypes": [ 1492 | { 1493 | "name": "value to text", 1494 | "value": 1 1495 | }, 1496 | { 1497 | "name": "range to text", 1498 | "value": 2 1499 | } 1500 | ], 1501 | "maxDataPoints": 100, 1502 | "nullPointMode": "connected", 1503 | "nullText": null, 1504 | "postfix": "", 1505 | "postfixFontSize": "50%", 1506 | "prefix": "", 1507 | "prefixFontSize": "50%", 1508 | "rangeMaps": [ 1509 | { 1510 | "from": "null", 1511 | "text": "N/A", 1512 | "to": "null" 1513 | } 1514 | ], 1515 | "sparkline": { 1516 | "fillColor": "rgba(31, 118, 189, 0.18)", 1517 | "full": false, 1518 | "lineColor": "rgb(31, 120, 193)", 1519 | "show": false 1520 | }, 1521 | "tableColumn": "", 1522 | "targets": [ 1523 | { 1524 | "groupBy": [], 1525 | "measurement": "drive_state", 1526 | "orderByTime": "ASC", 1527 | "policy": "default", 1528 | "query": "SELECT last(\"shift_state\") FROM \"drive_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) ", 1529 | "rawQuery": true, 1530 | "refId": "A", 1531 | "resultFormat": "time_series", 1532 | "select": [ 1533 | [ 1534 | { 1535 | "params": [ 1536 | "shift_state" 1537 | ], 1538 | "type": "field" 1539 | }, 1540 | { 1541 | "params": [], 1542 | "type": "last" 1543 | } 1544 | ] 1545 | ], 1546 | "tags": [ 1547 | { 1548 | "key": "metric", 1549 | "operator": "=", 1550 | "value": "shift_state" 1551 | }, 1552 | { 1553 | "condition": "AND", 1554 | "key": "display_name", 1555 | "operator": "=~", 1556 | "value": "/^$vehicle_name$/" 1557 | } 1558 | ] 1559 | } 1560 | ], 1561 | "thresholds": "", 1562 | "title": "Drivemode", 1563 | "type": "singlestat", 1564 | "valueFontSize": "80%", 1565 | "valueMaps": [ 1566 | { 1567 | "op": "=", 1568 | "text": "N/A", 1569 | "value": "null" 1570 | } 1571 | ], 1572 | "valueName": "avg" 1573 | }, 1574 | { 1575 | "backgroundColor": "rgba(128,128,128,0.1)", 1576 | "colorMaps": [ 1577 | { 1578 | "color": "#CCC", 1579 | "text": "N/A" 1580 | }, 1581 | { 1582 | "color": "#7EB26D", 1583 | "text": "online" 1584 | }, 1585 | { 1586 | "color": "#bf1b00", 1587 | "text": "offline" 1588 | }, 1589 | { 1590 | "color": "#6ED0E0", 1591 | "text": "asleep" 1592 | }, 1593 | { 1594 | "color": "#052b51", 1595 | "text": "waking" 1596 | } 1597 | ], 1598 | "crosshairColor": "#8F070C", 1599 | "datasource": "${DS_TESLA}", 1600 | "display": "timeline", 1601 | "expandFromQueryS": 3600, 1602 | "extendLastValue": true, 1603 | "gridPos": { 1604 | "h": 4, 1605 | "w": 24, 1606 | "x": 0, 1607 | "y": 16 1608 | }, 1609 | "hideTimeOverride": false, 1610 | "highlightOnMouseover": true, 1611 | "id": 20, 1612 | "legendSortBy": "-ms", 1613 | "lineColor": "rgba(0,0,0,0.1)", 1614 | "links": [], 1615 | "metricNameColor": "#000000", 1616 | "rangeMaps": [ 1617 | { 1618 | "from": "null", 1619 | "text": "N/A", 1620 | "to": "null" 1621 | } 1622 | ], 1623 | "rowHeight": 50, 1624 | "showLegend": true, 1625 | "showLegendNames": true, 1626 | "showLegendPercent": true, 1627 | "showLegendTime": true, 1628 | "showLegendValues": true, 1629 | "showTimeAxis": true, 1630 | "targets": [ 1631 | { 1632 | "groupBy": [], 1633 | "measurement": "vehicle_state", 1634 | "orderByTime": "ASC", 1635 | "policy": "default", 1636 | "query": "SELECT \"state\" FROM \"vehicle_state\" WHERE (\"display_name\" =~ /^$vehicle_name$/) AND $timeFilter-5d\n", 1637 | "rawQuery": true, 1638 | "refId": "A", 1639 | "resultFormat": "table", 1640 | "select": [ 1641 | [ 1642 | { 1643 | "params": [ 1644 | "state" 1645 | ], 1646 | "type": "field" 1647 | } 1648 | ] 1649 | ], 1650 | "tags": [ 1651 | { 1652 | "key": "metric", 1653 | "operator": "=", 1654 | "value": "state" 1655 | }, 1656 | { 1657 | "condition": "AND", 1658 | "key": "display_name", 1659 | "operator": "=~", 1660 | "value": "/^$vehicle_name$/" 1661 | } 1662 | ] 1663 | } 1664 | ], 1665 | "textSize": 24, 1666 | "textSizeTime": 12, 1667 | "timeFrom": null, 1668 | "timeOptions": [ 1669 | { 1670 | "name": "Years", 1671 | "value": "years" 1672 | }, 1673 | { 1674 | "name": "Months", 1675 | "value": "months" 1676 | }, 1677 | { 1678 | "name": "Weeks", 1679 | "value": "weeks" 1680 | }, 1681 | { 1682 | "name": "Days", 1683 | "value": "days" 1684 | }, 1685 | { 1686 | "name": "Hours", 1687 | "value": "hours" 1688 | }, 1689 | { 1690 | "name": "Minutes", 1691 | "value": "minutes" 1692 | }, 1693 | { 1694 | "name": "Seconds", 1695 | "value": "seconds" 1696 | }, 1697 | { 1698 | "name": "Milliseconds", 1699 | "value": "milliseconds" 1700 | } 1701 | ], 1702 | "timePrecision": { 1703 | "name": "Minutes", 1704 | "value": "minutes" 1705 | }, 1706 | "timeShift": null, 1707 | "timeTextColor": "#d8d9da", 1708 | "title": "Sleepstate", 1709 | "type": "natel-discrete-panel", 1710 | "units": "short", 1711 | "useTimePrecision": false, 1712 | "valueMaps": [ 1713 | { 1714 | "op": "=", 1715 | "text": "N/A", 1716 | "value": "null" 1717 | } 1718 | ], 1719 | "valueTextColor": "#000000", 1720 | "writeAllValues": false, 1721 | "writeLastValue": true, 1722 | "writeMetricNames": false 1723 | }, 1724 | { 1725 | "backgroundColor": "rgba(128,128,128,0.1)", 1726 | "colorMaps": [ 1727 | { 1728 | "color": "#BDD", 1729 | "text": "???" 1730 | }, 1731 | { 1732 | "color": "rgb(76, 76, 76)", 1733 | "text": "Park" 1734 | }, 1735 | { 1736 | "color": "#bf1b00", 1737 | "text": "Reverse" 1738 | }, 1739 | { 1740 | "color": "#508642", 1741 | "text": "Drive" 1742 | }, 1743 | { 1744 | "color": "#BDD", 1745 | "text": "Neutral" 1746 | } 1747 | ], 1748 | "crosshairColor": "#8F070C", 1749 | "datasource": "${DS_TESLA}", 1750 | "display": "timeline", 1751 | "expandFromQueryS": null, 1752 | "extendLastValue": true, 1753 | "gridPos": { 1754 | "h": 4, 1755 | "w": 24, 1756 | "x": 0, 1757 | "y": 20 1758 | }, 1759 | "highlightOnMouseover": true, 1760 | "id": 24, 1761 | "legendSortBy": "-ms", 1762 | "lineColor": "rgba(0,0,0,0.1)", 1763 | "links": [], 1764 | "metricNameColor": "#000000", 1765 | "rangeMaps": [ 1766 | { 1767 | "from": "null", 1768 | "text": "N/A", 1769 | "to": "null" 1770 | } 1771 | ], 1772 | "rowHeight": 50, 1773 | "showLegend": true, 1774 | "showLegendNames": true, 1775 | "showLegendPercent": true, 1776 | "showLegendTime": true, 1777 | "showLegendValues": true, 1778 | "showTimeAxis": true, 1779 | "targets": [ 1780 | { 1781 | "groupBy": [], 1782 | "measurement": "drive_state", 1783 | "orderByTime": "ASC", 1784 | "policy": "default", 1785 | "query": "SELECT \"shift_state\" FROM \"drive_state\" WHERE (\"metric\" = 'shift_state' AND \"display_name\" =~ /^$vehicle_name$/) AND $timeFilter", 1786 | "rawQuery": false, 1787 | "refId": "A", 1788 | "resultFormat": "time_series", 1789 | "select": [ 1790 | [ 1791 | { 1792 | "params": [ 1793 | "shift_state" 1794 | ], 1795 | "type": "field" 1796 | } 1797 | ] 1798 | ], 1799 | "tags": [ 1800 | { 1801 | "key": "display_name", 1802 | "operator": "=~", 1803 | "value": "/^$vehicle_name$/" 1804 | } 1805 | ] 1806 | } 1807 | ], 1808 | "textSize": 24, 1809 | "textSizeTime": 12, 1810 | "timeFrom": null, 1811 | "timeOptions": [ 1812 | { 1813 | "name": "Years", 1814 | "value": "years" 1815 | }, 1816 | { 1817 | "name": "Months", 1818 | "value": "months" 1819 | }, 1820 | { 1821 | "name": "Weeks", 1822 | "value": "weeks" 1823 | }, 1824 | { 1825 | "name": "Days", 1826 | "value": "days" 1827 | }, 1828 | { 1829 | "name": "Hours", 1830 | "value": "hours" 1831 | }, 1832 | { 1833 | "name": "Minutes", 1834 | "value": "minutes" 1835 | }, 1836 | { 1837 | "name": "Seconds", 1838 | "value": "seconds" 1839 | }, 1840 | { 1841 | "name": "Milliseconds", 1842 | "value": "milliseconds" 1843 | } 1844 | ], 1845 | "timePrecision": { 1846 | "name": "Minutes", 1847 | "value": "minutes" 1848 | }, 1849 | "timeTextColor": "#d8d9da", 1850 | "title": "Drive Mode", 1851 | "type": "natel-discrete-panel", 1852 | "units": "short", 1853 | "useTimePrecision": false, 1854 | "valueMaps": [ 1855 | { 1856 | "op": "=", 1857 | "text": "Park", 1858 | "value": "P" 1859 | }, 1860 | { 1861 | "op": "=", 1862 | "text": "Drive", 1863 | "value": "D" 1864 | }, 1865 | { 1866 | "op": "=", 1867 | "text": "Neutral", 1868 | "value": "N" 1869 | }, 1870 | { 1871 | "op": "=", 1872 | "text": "Reverse", 1873 | "value": "R" 1874 | } 1875 | ], 1876 | "valueTextColor": "#000000", 1877 | "writeAllValues": false, 1878 | "writeLastValue": true, 1879 | "writeMetricNames": false 1880 | }, 1881 | { 1882 | "backgroundColor": "rgba(128,128,128,0.1)", 1883 | "colorMaps": [ 1884 | { 1885 | "color": "#CCC", 1886 | "text": "N/A" 1887 | }, 1888 | { 1889 | "color": "#7EB26D", 1890 | "text": "false" 1891 | }, 1892 | { 1893 | "color": "#bf1b00", 1894 | "text": "true" 1895 | } 1896 | ], 1897 | "crosshairColor": "#8F070C", 1898 | "datasource": "${DS_TESLA}", 1899 | "display": "timeline", 1900 | "expandFromQueryS": 0, 1901 | "extendLastValue": true, 1902 | "gridPos": { 1903 | "h": 5, 1904 | "w": 24, 1905 | "x": 0, 1906 | "y": 24 1907 | }, 1908 | "highlightOnMouseover": true, 1909 | "id": 26, 1910 | "legendSortBy": "-ms", 1911 | "lineColor": "rgba(0,0,0,0.1)", 1912 | "links": [], 1913 | "metricNameColor": "#000000", 1914 | "rangeMaps": [ 1915 | { 1916 | "from": "null", 1917 | "text": "N/A", 1918 | "to": "null" 1919 | } 1920 | ], 1921 | "rowHeight": 50, 1922 | "showDistinctCount": false, 1923 | "showLegend": true, 1924 | "showLegendNames": true, 1925 | "showLegendPercent": true, 1926 | "showLegendTime": true, 1927 | "showLegendValues": true, 1928 | "showTimeAxis": true, 1929 | "showTransitionCount": false, 1930 | "targets": [ 1931 | { 1932 | "alias": "Battery Heater", 1933 | "groupBy": [], 1934 | "measurement": "charge_state", 1935 | "orderByTime": "ASC", 1936 | "policy": "default", 1937 | "refId": "A", 1938 | "resultFormat": "time_series", 1939 | "select": [ 1940 | [ 1941 | { 1942 | "params": [ 1943 | "battery_heater_on" 1944 | ], 1945 | "type": "field" 1946 | } 1947 | ] 1948 | ], 1949 | "tags": [ 1950 | { 1951 | "key": "display_name", 1952 | "operator": "=~", 1953 | "value": "/^$vehicle_name$/" 1954 | } 1955 | ] 1956 | } 1957 | ], 1958 | "textSize": 24, 1959 | "textSizeTime": 12, 1960 | "timeOptions": [ 1961 | { 1962 | "name": "Years", 1963 | "value": "years" 1964 | }, 1965 | { 1966 | "name": "Months", 1967 | "value": "months" 1968 | }, 1969 | { 1970 | "name": "Weeks", 1971 | "value": "weeks" 1972 | }, 1973 | { 1974 | "name": "Days", 1975 | "value": "days" 1976 | }, 1977 | { 1978 | "name": "Hours", 1979 | "value": "hours" 1980 | }, 1981 | { 1982 | "name": "Minutes", 1983 | "value": "minutes" 1984 | }, 1985 | { 1986 | "name": "Seconds", 1987 | "value": "seconds" 1988 | }, 1989 | { 1990 | "name": "Milliseconds", 1991 | "value": "milliseconds" 1992 | } 1993 | ], 1994 | "timePrecision": { 1995 | "name": "Minutes", 1996 | "value": "minutes" 1997 | }, 1998 | "timeTextColor": "#d8d9da", 1999 | "title": "Battery Heater", 2000 | "type": "natel-discrete-panel", 2001 | "units": "short", 2002 | "useTimePrecision": false, 2003 | "valueMaps": [ 2004 | { 2005 | "op": "=", 2006 | "text": "N/A", 2007 | "value": "null" 2008 | }, 2009 | { 2010 | "op": "=", 2011 | "text": "On", 2012 | "value": "true" 2013 | }, 2014 | { 2015 | "op": "=", 2016 | "text": "Off", 2017 | "value": "false" 2018 | } 2019 | ], 2020 | "valueTextColor": "#000000", 2021 | "writeAllValues": false, 2022 | "writeLastValue": true, 2023 | "writeMetricNames": false 2024 | } 2025 | ], 2026 | "refresh": false, 2027 | "schemaVersion": 16, 2028 | "style": "dark", 2029 | "tags": [ 2030 | "tesla" 2031 | ], 2032 | "templating": { 2033 | "list": [ 2034 | { 2035 | "allValue": null, 2036 | "current": {}, 2037 | "datasource": "${DS_TESLA}", 2038 | "definition": "select distinct(display_name) from (select * from charge_state)", 2039 | "hide": 0, 2040 | "includeAll": false, 2041 | "label": null, 2042 | "multi": false, 2043 | "name": "vehicle_name", 2044 | "options": [], 2045 | "query": "select distinct(display_name) from (select * from charge_state)", 2046 | "refresh": 1, 2047 | "regex": "", 2048 | "skipUrlSync": false, 2049 | "sort": 0, 2050 | "tagValuesQuery": "", 2051 | "tags": [], 2052 | "tagsQuery": "", 2053 | "type": "query", 2054 | "useTags": false 2055 | }, 2056 | { 2057 | "current": { 2058 | "value": "${VAR_RANGEUNIT}", 2059 | "text": "${VAR_RANGEUNIT}" 2060 | }, 2061 | "hide": 2, 2062 | "label": null, 2063 | "name": "rangeunit", 2064 | "options": [ 2065 | { 2066 | "value": "${VAR_RANGEUNIT}", 2067 | "text": "${VAR_RANGEUNIT}" 2068 | } 2069 | ], 2070 | "query": "${VAR_RANGEUNIT}", 2071 | "skipUrlSync": false, 2072 | "type": "constant" 2073 | }, 2074 | { 2075 | "allValue": null, 2076 | "current": { 2077 | "text": "1.60934", 2078 | "value": "1.60934" 2079 | }, 2080 | "hide": 2, 2081 | "includeAll": false, 2082 | "label": null, 2083 | "multi": false, 2084 | "name": "rangefactor", 2085 | "options": [ 2086 | { 2087 | "selected": true, 2088 | "text": "1.60934", 2089 | "value": "1.60934" 2090 | } 2091 | ], 2092 | "query": "1.60934", 2093 | "skipUrlSync": false, 2094 | "type": "custom" 2095 | } 2096 | ] 2097 | }, 2098 | "time": { 2099 | "from": "now-7d", 2100 | "to": "now" 2101 | }, 2102 | "timepicker": { 2103 | "refresh_intervals": [ 2104 | "5s", 2105 | "10s", 2106 | "30s", 2107 | "1m", 2108 | "5m", 2109 | "15m", 2110 | "30m", 2111 | "1h", 2112 | "2h", 2113 | "1d" 2114 | ], 2115 | "time_options": [ 2116 | "5m", 2117 | "15m", 2118 | "1h", 2119 | "6h", 2120 | "12h", 2121 | "24h", 2122 | "2d", 2123 | "7d", 2124 | "30d" 2125 | ] 2126 | }, 2127 | "timezone": "", 2128 | "title": "Driving", 2129 | "uid": "Imf45f8iz", 2130 | "version": 148 2131 | } 2132 | --------------------------------------------------------------------------------