-
Notifications
You must be signed in to change notification settings - Fork 66
/
setup.sh
executable file
·560 lines (517 loc) · 17.7 KB
/
setup.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
#!/bin/bash
#
# Interactive Setup Script for Powerwall Dashboard
# by Jason Cox - 21 Jan 2022
# Stop on Errors
set -e
# Set Globals
COMPOSE_ENV_FILE="compose.env"
INFLUXDB_ENV_FILE="influxdb.env"
TELEGRAF_LOCAL="telegraf.local"
PW_ENV_FILE="pypowerwall.env"
GF_ENV_FILE="grafana.env"
PW_STYLE="grafana-dark"
if [ ! -f VERSION ]; then
echo "ERROR: Missing VERSION file. Setup must run from installation directory."
echo ""
exit 1
fi
VERSION=`cat VERSION`
echo "Powerwall Dashboard (v${VERSION}) - SETUP"
echo "-----------------------------------------"
# Verify not running as root
if [ "$EUID" -eq 0 ]; then
echo "ERROR: Running this as root will cause permission issues."
echo ""
echo "Please ensure your local user is in the docker group and run without sudo."
echo " sudo usermod -aG docker \$USER"
echo " $0"
echo ""
exit 1
fi
# Verify user has write permission to this directory
if [ ! -w . ]; then
echo "ERROR: Your user ($USER) does not have write permission to this directory."
echo ""
ls -ld "$(pwd)"
echo ""
echo "Please fix file permissions and try again."
echo ""
exit 1
fi
# Verify user in docker group (not required for Windows Git Bash)
if ! type winpty > /dev/null 2>&1; then
if ! $(id -Gn 2>/dev/null | grep -qw "docker"); then
echo "WARNING: You do not appear to be in the docker group."
echo ""
echo "Please ensure your local user is in the docker group and run without sudo."
echo " sudo usermod -aG docker \$USER"
echo " $0"
echo ""
read -r -p "Setup - Proceed? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
echo ""
else
echo "Cancel"
exit 1
fi
fi
fi
# Windows Git Bash docker exec compatibility fix
if type winpty > /dev/null 2>&1; then
shopt -s expand_aliases
alias docker="winpty -Xallow-non-tty -Xplain docker"
fi
# Service Running Helper Function
running() {
local url=${1:-http://localhost:80}
local code=${2:-200}
local status=$(curl --head --location --connect-timeout 5 --write-out %{http_code} --silent --output /dev/null ${url})
[[ $status == ${code} ]]
}
# Get latitude and longitude
LAT="0.0"
LONG="0.0"
PYTHON=$(command -v python3 || command -v python)
if [ -n "${PYTHON}" ]; then
IP_RESPONSE=$(curl -s https://freeipapi.com/api/json)
LAT=$(echo "$IP_RESPONSE" | "${PYTHON}" -c "import sys, json; print(json.load(sys.stdin)['latitude'])")
LONG=$(echo "$IP_RESPONSE" | "${PYTHON}" -c "import sys, json; print(json.load(sys.stdin)['longitude'])")
fi
# Docker Dependency Check
if ! docker info > /dev/null 2>&1; then
echo "ERROR: docker is not available or not running."
echo "This script requires docker, please install and try again."
exit 1
fi
# Verify Docker Compose Version
if ! docker compose version > /dev/null 2>&1; then
if docker-compose version > /dev/null 2>&1; then
# Check for version information to see if it is not V1
COMPOSE_VERSION=`docker-compose version --short`
if [[ "${COMPOSE_VERSION}" == "1"* ]]; then
# Build Docker (v1)
echo "ERROR: Docker Compose V1 Found: Upgrade Required"
echo "See Migration Instructions at https://docs.docker.com/compose/migrate/"
exit 1
fi
else
echo "ERROR: Docker Compose is not available."
echo "This script requires Docker Compose."
echo "Please install and try again."
exit 1
fi
fi
# Command Line Options
while getopts ":hf" opt; do
case ${opt} in
h )
echo "Usage: setup.sh [-h]"
echo " -h Display this help message."
echo " -f Setup FleetAPI Cloud Mode"
exit 0
;;
f )
echo "Setup FleetAPI Cloud Mode"
echo ""
if [ ! -f ${PW_ENV_FILE} ]; then
echo "ERROR: Missing ${PW_ENV_FILE}. Run setup.sh first."
echo ""
exit 1
fi
echo "This will configure FleetAPI Cloud mode for Powerwall systems."
echo ""
read -r -p "Setup FleetAPI Cloud Mode? [Y/n] " response
if [[ "$response" =~ ^([nN][oO]|[nN])$ ]]; then
echo "Cancel"
exit 1
fi
echo ""
echo "Running FleetAPI Cloud Mode Setup..."
echo ""
docker exec -it pypowerwall python3 -m pypowerwall fleetapi
echo ""
echo "Restarting..."
docker restart pypowerwall
echo "-----------------------------------------"
exit 0
;;
\? )
echo "Invalid Option: -$OPTARG" 1>&2
exit 1
;;
esac
done
# Check PW_ENV_FILE for existing configuration
if [ ! -f ${PW_ENV_FILE} ]; then
choice=""
config="None"
elif grep -qE "^PW_HOST=.+" "${PW_ENV_FILE}"; then
choice="[1] "
config="Local Access"
else
choice="[2] "
config="Tesla Cloud"
fi
# Prompt for configuration
echo "Select configuration mode:"
echo ""
echo "Current: ${config}"
echo ""
echo " 1 - Local Access (Powerwall 1, 2, or + using the Tesla Gateway on LAN) - Default"
echo " 2 - Tesla Cloud (Solar-only systems or Powerwalls without LAN access)"
echo " 3 - FleetAPI Cloud (Powerwall systems using Official Telsa API)"
echo " 4 - Powerwall 3 (Powerwall 3 using the local Tesla Gateway)"
echo ""
pw3=0
while :; do
read -r -p "Select mode: ${choice}" response
if [ "${response}" == "1" ]; then
selected="Local Access"
elif [ "${response}" == "2" ]; then
selected="Tesla Cloud"
elif [ "${response}" == "3" ]; then
selected="FleetAPI Cloud"
elif [ "${response}" == "4" ]; then
selected="Local Access"
pw3=1
elif [ -z "${response}" ] && [ ! -z "${choice}" ]; then
selected="${config}"
else
continue
fi
if [ ! -z "${choice}" ] && [ "${selected}" != "${config}" ]; then
echo ""
read -r -p "You are already using the ${config} configuration, are you sure you wish to change? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
config="${selected}"
rm ${PW_ENV_FILE}
echo ""
break
else
echo "Cancel"
exit 1
fi
else
config="${selected}"
echo ""
break
fi
done
# Create default docker compose env file if needed.
if [ ! -f ${COMPOSE_ENV_FILE} ]; then
cp "${COMPOSE_ENV_FILE}.sample" "${COMPOSE_ENV_FILE}"
fi
# Check if running as non-default user (not required for Windows Git Bash)
if ! type winpty > /dev/null 2>&1; then
notset=0
if [ -z "${PWD_USER}" ]; then
PWD_USER="1000:1000"
notset=1
fi
if [ "${PWD_USER}" == "1000:1000" ]; then
desc="normal default"
name="Default"
else
desc="configured user"
name=" Saved"
fi
CURRENT_USER="$(id -u):$(id -g)"
if [ "${CURRENT_USER}" != "${PWD_USER}" ]; then
echo "WARNING: Your current user uid/gid does not match the ${desc}."
echo ""
echo "Current - ${CURRENT_USER}"
echo "${name} - ${PWD_USER}"
echo ""
echo "Do you wish to configure the dashboard to run as the current user?"
echo "(this is typically required to avoid permission issues)"
echo ""
read -r -p "Run as current user? (${CURRENT_USER}) [Y/n] " response
if [[ "$response" =~ ^([nN][oO]|[nN])$ ]]; then
echo ""
echo "No problem. If you have permission issues, edit ${COMPOSE_ENV_FILE} or re-run setup."
echo ""
else
# Update PWD_USER and save to docker compose env file
if [ $notset -eq 1 ]; then
if grep -q "^#PWD_USER=" "${COMPOSE_ENV_FILE}"; then
sed -i.bak "s@^#PWD_USER=.*@PWD_USER=\"${CURRENT_USER}\"@g" "${COMPOSE_ENV_FILE}"
else
echo -e "\nPWD_USER=\"${CURRENT_USER}\"" >> "${COMPOSE_ENV_FILE}"
fi
else
sed -i.bak "s@^PWD_USER=.*@PWD_USER=\"${CURRENT_USER}\"@g" "${COMPOSE_ENV_FILE}"
fi
echo ""
fi
fi
fi
# Check for RPi Issue with Buster
if [[ -f "/etc/os-release" ]]; then
OS_VER=`grep PRETTY /etc/os-release | cut -d= -f2 | cut -d\" -f2`
if [[ "$OS_VER" == "Raspbian GNU/Linux 10 (buster)" ]]
then
echo "WARNING: You are running ${OS_VER}"
echo " This OS version has a bug in the libseccomp2 library that"
echo " causes the pypowerwall container to fail."
echo " See details: https://github.com/jasonacox/Powerwall-Dashboard/issues/56"
echo ""
read -r -p "Setup - Proceed? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
echo ""
else
echo "Cancel"
exit 1
fi
fi
fi
CURRENT=`cat tz`
echo "Timezone (leave blank for ${CURRENT})"
read -p 'Enter Timezone: ' TZ
echo ""
# Powerwall Credentials
if [ -f ${PW_ENV_FILE} ]; then
echo "Current Credentials:"
echo ""
cat ${PW_ENV_FILE}
echo ""
read -r -p "Update these credentials? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
rm ${PW_ENV_FILE}
echo ""
else
echo "Using existing ${PW_ENV_FILE}."
fi
fi
# Function to test a GW IP to see if it responds
function test_ip() {
local IP=$1
if [ -z "${IP}" ]; then
return 1
fi
if curl -k --head --connect-timeout 2 --silent https://${IP} > /dev/null 2>&1; then
return 0
else
return 1
fi
}
# Create Powerwall Settings
if [ ! -f ${PW_ENV_FILE} ]; then
if [ "${config}" == "Local Access" ]; then
if [ $pw3 -eq 1 ]; then
echo "Setting credentials for Powerwall 3..."
PASSWORD=""
EMAIL=""
else
echo "Enter credentials for Powerwall..."
while [ -z "${PASSWORD}" ]; do
read -p 'Password: ' PASSWORD
done
while [ -z "${EMAIL}" ]; do
read -p 'Email: ' EMAIL
done
fi
IP=""
# Can we reach 192.168.91.1
if test_ip "192.168.91.1"; then
IP="192.168.91.1"
echo "Found Powerwall Gateway at ${IP}"
read -p 'Use this IP? [Y/n] ' response
if [[ "$response" =~ ^([nN][oO]|[nN])$ ]]; then
IP=""
else
echo ""
echo "Congratulations!"
echo "Extended Device Metrics (vitals) are available on this endpoint via TEDAPI."
echo "However, you will need the Gateway password to access them."
echo "This password is often on the QR code on the Powerwall Gateway unit."
echo ""
read -p 'Enter Gateway Password or leave blank to disable: ' PW
if [ -z "${PW}" ]; then
PW_GW_PWD=""
else
PW_GW_PWD="${PW}"
fi
echo ""
# Double check the user doesn't have a Powerwall 3
if [ $pw3 -ne 1 ]; then
read -p 'Do you have a Powerwall 3? [y/N] ' response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
pw3=1
PASSWORD=""
EMAIL=""
fi
echo ""
fi
fi
else
echo "The Powerwall Gateway (192.168.91.1) is not found on your LAN."
if [ $pw3 -eq 1 ]; then
echo ""
echo "Powerwall 3 requires access to the Gateway for pull local data."
echo "Ensure the Gateway can be reached by your host and rerun setup."
echo "Alternatively you can select a Tesla Cloud mode."
echo ""
echo "Test: curl -k --head https://192.168.91.1"
echo ""
exit 1
fi
echo "Standard dashboard metrics will work but Extended data (vitals) via TEDAPI"
echo "will not be available. Consult the project for information on how to enable."
echo "Proceeding with standard metrics..."
echo ""
fi
if [ -z "${IP}" ]; then
read -p 'Powerwall IP Address (leave blank to scan network): ' IP
fi
else
echo "Enter email address for Tesla Account..."
while [ -z "${EMAIL}" ]; do
read -p 'Email: ' EMAIL
done
echo "If you have a Solar-only system, you can customize the dashboard for Solar and"
echo "hide the Powerwall metrics."
echo ""
# ask if user has a solar only system
read -p 'Set dashboard to Solar-only system? [y/N] ' response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
PW_STYLE="solar"
fi
fi
echo "PW_EMAIL=${EMAIL}" > ${PW_ENV_FILE}
echo "PW_PASSWORD=${PASSWORD}" >> ${PW_ENV_FILE}
echo "PW_HOST=${IP}" >> ${PW_ENV_FILE}
echo "PW_TIMEZONE=America/Los_Angeles" >> ${PW_ENV_FILE}
echo "TZ=America/Los_Angeles" >> ${PW_ENV_FILE}
echo "PW_DEBUG=no" >> ${PW_ENV_FILE}
echo "PW_STYLE=${PW_STYLE}" >> ${PW_ENV_FILE}
if [ ! -z "${PW_GW_PWD}" ]; then
echo "PW_GW_PWD=${PW_GW_PWD}" >> ${PW_ENV_FILE}
fi
fi
# Create default telegraf local file if needed.
if [ ! -f ${TELEGRAF_LOCAL} ]; then
cp "${TELEGRAF_LOCAL}.sample" "${TELEGRAF_LOCAL}"
fi
# Create InfluxDB env file if missing (required in 3.0.7)
if [ ! -f ${INFLUXDB_ENV_FILE} ]; then
cp "${INFLUXDB_ENV_FILE}.sample" "${INFLUXDB_ENV_FILE}"
fi
# Create Grafana Settings if missing (required in 2.4.0)
if [ ! -f ${GF_ENV_FILE} ]; then
cp "${GF_ENV_FILE}.sample" "${GF_ENV_FILE}"
fi
echo ""
if [ -z "${TZ}" ]; then
echo "Using ${CURRENT} timezone..."
./tz.sh "${CURRENT}"
else
echo "Setting ${TZ} timezone..."
./tz.sh "${TZ}"
fi
echo "-----------------------------------------"
echo ""
# Optional - Setup Weather Data
if [ -f weather.sh ]; then
./weather.sh setup
fi
if [ -f grafana/sunandmoon-template.yml ]; then
cp grafana/sunandmoon-template.yml grafana/provisions/datasources/sunandmoon.yml
sed -i.bak "s@zzLAT@${LAT}@g" grafana/provisions/datasources/sunandmoon.yml
sed -i.bak "s@zzLONG@${LONG}@g" grafana/provisions/datasources/sunandmoon.yml
# Remove backup file
rm grafana/provisions/datasources/sunandmoon.yml.bak
fi
# Build Docker in current environment
./compose-dash.sh up -d
echo "-----------------------------------------"
# Run Local Access mode network scan
if [ "${config}" == "Local Access" ] && ! grep -qE "^PW_HOST=.+" "${PW_ENV_FILE}"; then
echo "Running network scan... (press Ctrl-C to interrupt)"
# Get local IP based on the operating system
OS=$(uname -s)
case $OS in
Linux*) IP=$(ip route get 8.8.8.8 2>/dev/null | awk '{print $7}') ;;
Darwin*) IP=$(ifconfig 2>/dev/null | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}') ;;
CYGWIN*|MINGW*|MSYS*) IP=$(netstat -rn 2>/dev/null | grep "0.0.0.0" | awk '{print $4}' | head -1) ;;
*) IP=""
esac
docker exec -it pypowerwall python3 -m pypowerwall scan -ip=${IP}
echo "-----------------------------------------"
echo "Enter address for Powerwall... (or leave blank to switch to Tesla Cloud mode)"
read -p 'IP Address: ' IP
echo ""
if [ -z "${IP}" ]; then
config="Tesla Cloud"
fi
sed -i.bak "s@^PW_HOST=.*@PW_HOST=${IP}@g" "${PW_ENV_FILE}"
./compose-dash.sh up -d
echo "-----------------------------------------"
fi
# Run Tesla Cloud mode setup
if [ "${config}" == "Tesla Cloud" ]; then
docker exec -it pypowerwall python3 -m pypowerwall setup -email=$(grep -E "^PW_EMAIL=.+" "${PW_ENV_FILE}" | cut -d= -f2)
echo "Restarting..."
docker restart pypowerwall
echo "-----------------------------------------"
fi
# Run FleetAPI mode setup
if [ "${config}" == "FleetAPI Cloud" ]; then
docker exec -it pypowerwall python3 -m pypowerwall fleetapi
echo "Restarting..."
docker restart pypowerwall
echo "-----------------------------------------"
fi
# Set up Influx
echo "Waiting for InfluxDB to start..."
until running http://localhost:8086/ping 204 2>/dev/null; do
printf '.'
sleep 5
done
echo " up!"
sleep 2
echo "Setup InfluxDB Data..."
docker exec --tty influxdb sh -c "influx -import -path=/var/lib/influxdb/influxdb.sql"
sleep 2
# Execute Run-Once queries for initial setup.
cd influxdb
for f in run-once*.sql; do
if [ ! -f "${f}.done" ]; then
echo "Executing single run query $f file..."
docker exec --tty influxdb sh -c "influx -import -path=/var/lib/influxdb/${f}"
echo "OK" > "${f}.done"
fi
done
cd ..
# Restart weather411 to force a sample
if [ -f weather/weather411.conf ]; then
echo "Fetching local weather..."
docker restart weather411
fi
# Display Final Instructions
if ! grep -qE "^PW_HOST=.+" "${PW_ENV_FILE}"
then
DASHBOARD="'dashboard.json' or 'dashboard-solar-only.json'"
else
DASHBOARD="'dashboard.json'"
fi
cat << EOF
------------------[ Final Setup Instructions ]-----------------
Open Grafana at http://localhost:9000/ ... use admin/admin for login.
To complete *Grafana Setup*:
* From 'Dashboard\Browse', select 'New/Import', browse to ${PWD}/dashboards
and upload ${DASHBOARD}.
NOTE: The datasources for InfluxDB and SunAndMoon are already configured.
If you need to modify them via Configuration\Data Sources:
* InfluxDB
- URL: 'http://influxdb:8086'
- Database: 'powerwall'
- Min time interval: '5s'
- Click "Save & test" button
* Sun and Moon
- Enter your latitude and longitude (tool here: https://bit.ly/3wYNaI1 )
- Click "Save & test" button
EOF