Compare commits

...
80 Commits
Author SHA1 Message Date
renovate[bot]andColin Seymour ff39e95482 Update TeslaMate to v4.1.1 (#150)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Colin Seymour <colin@symr.io>
2026-08-19 17:21:24 +01:00
renovate[bot]andColin Seymour 8b048671cb Update frenck/action-addon-linter action to v2.21.1 (#151)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Colin Seymour <colin@symr.io>
2026-08-19 17:18:41 +01:00
Colin Seymour 932352d43a Update integration tests and update dev environment (#152) 2026-08-19 17:14:15 +01:00
renovate[bot] b21b77c5b0 Update actions/checkout action to v7.0.1 (#149)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-21 06:35:27 +01:00
renovate[bot] 0de3dd5aa2 Update softprops/action-gh-release action to v3.0.2 (#148)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 20:08:33 +01:00
Colin Seymour 6b07490526 Update and improve dev env, testing, building and publishing (#143) 2026-06-21 13:05:21 +01:00
Colin Seymour a5b5699b49 Configure Renovate to pin GitHub Actions to SHAs (#147) 2026-06-21 12:55:57 +01:00
renovate[bot] d6d3beb3ee Update actions/checkout action to v7 (#145)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 10:52:30 +01:00
renovate[bot] 6a20a1e31d Update TeslaMate to v4 (#142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-15 08:59:01 +01:00
renovate[bot] 2e360aab07 Update actions/checkout action to v6.0.3 (#138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-02 16:31:24 +01:00
renovate[bot] 0610675d2b Update softprops/action-gh-release action to v3 (#136) 2026-05-23 07:58:58 +01:00
renovate[bot] 371596126d Update docker/login-action action to v4.2.0 (#137)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-22 15:23:01 +01:00
renovate[bot] 4787650bbf Update docker/login-action action to v4.1.0 (#134) 2026-04-03 09:11:35 +01:00
renovate[bot] 5b57a82b6a Update home-assistant/builder action to v2026.03.2 (#133) 2026-03-18 07:23:56 +00:00
renovate[bot] 157dbc81c2 Update home-assistant/builder action to v2026.03.0 (#132)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-17 13:11:22 +00:00
renovate[bot] 4e5eab5bd9 Update softprops/action-gh-release action to v2.6.1 (#131)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-16 09:40:50 +00:00
renovate[bot] 2456784d56 Update actions/checkout action to v6.0.2 (#125)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-06 15:08:06 +00:00
renovate[bot] f7b4745a20 Update TeslaMate to v3 (#129)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-06 15:07:49 +00:00
renovate[bot] bdc682d040 Update docker/login-action action to v4 (#130)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-04 09:56:41 +00:00
renovate[bot] 0bd729517b Update home-assistant/builder action to v2026.02.1 (#128)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 17:53:21 +00:00
renovate[bot] 145e7e6807 Update home-assistant/builder action to v2026 (#127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 13:07:11 +00:00
renovate[bot] d42b7aa0e8 Update docker/login-action action to v3.7.0 (#126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-28 15:51:16 +00:00
renovate[bot] 320389ea16 Update actions/checkout action to v6.0.1 (#121) 2025-12-03 07:48:05 +00:00
renovate[bot] 61c0a0fada Update softprops/action-gh-release action to v2.5.0 (#116)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 09:41:09 +00:00
renovate[bot] d7cea32f07 Update home-assistant/builder action to v2025.11.0 (#120)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 10:13:33 +00:00
renovate[bot] 1baaf635f5 Update actions/checkout action to v6 (#119)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-20 18:13:09 +00:00
renovate[bot] eec7ac2822 Update actions/checkout action to v5.0.1 (#118)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-18 09:21:35 +00:00
renovate[bot] 7a86a1f0c3 Update TeslaMate to v2.2.0 (#114)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-08 12:24:13 +00:00
Colin Seymour 83c29fbed8 Drop armv7 support and codenotary (#115)
* Drop armv7

* Remove codenotary
2025-11-08 12:19:26 +00:00
Colin Seymour ee6faf280d Add note for clearing TeslaMate config to re-show config links (#113)
Add note for clearing TeslaMate config for logs

Added note about clearing TeslaMate configuration for log visibility.
2025-10-17 10:43:50 +01:00
Niels 2421d0a805 Remove duplicated custom_plugins: [] (#110) 2025-10-14 09:45:12 +01:00
renovate[bot] aa5dee6c4a Update softprops/action-gh-release action to v2.4.1 (#109)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 10:33:48 +01:00
renovate[bot] 77e7510e3b Update softprops/action-gh-release action to v2.4.0 (#108)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 09:18:43 +01:00
renovate[bot] 6031fe6fa4 Update docker/login-action action to v3.6.0 (#106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-05 07:22:50 +01:00
renovate[bot] eb7945344e Update softprops/action-gh-release action to v2.3.4 (#107)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-05 07:22:36 +01:00
renovate[bot] e9c77210b7 Update home-assistant/builder action to v2025.09.0 (#104)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-16 09:52:27 +01:00
renovate[bot] 64cbe20138 Update softprops/action-gh-release action to v2.3.3 (#103)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-07 12:20:32 +01:00
renovate[bot] 1c521e808c Update TeslaMate to v2.1.1 (#100)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-05 09:34:41 +01:00
renovate[bot] 0d7507b3d9 Update actions/checkout action to v5 (#98)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 14:56:40 +01:00
renovate[bot] d8aa9cb7d9 Update actions/checkout action to v4.3.0 (#97)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 14:53:25 +01:00
renovate[bot] 2dfda5bbc0 Update docker/login-action action to v3.5.0 (#96)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-04 16:43:20 +01:00
renovate[bot] 522e9890ca Update TeslaMate to v2.1.0 (#94)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 16:19:45 +01:00
Colin Seymour 453252d864 Only set file limit to a max of 524288 (#93) 2025-07-07 09:14:42 +01:00
renovate[bot]andColin Seymour 800d3ee3ab Update TeslaMate to v2 (#85)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Colin Seymour <colin@symr.io>
2025-07-06 09:22:01 +01:00
Colin Seymour 562fd279b0 Prep for TeslaMate v2 update (#91)
* Prep docs for v2

* Add pg version check

* Import dashboards after DB

* Assume compatible

* Emphasis version

* Use 2 spaces for ident

* Improve logging
2025-07-06 09:13:44 +01:00
renovate[bot] 9d0729c9bf Update softprops/action-gh-release action to v2.3.2 (#90) 2025-06-11 08:49:21 +01:00
renovate[bot] e3a352b44d Update softprops/action-gh-release action to v2.3.0 (#89) 2025-06-10 07:53:17 +01:00
renovate[bot] fc5f6d6c2d Update softprops/action-gh-release action to v2.2.2 (#84) 2025-04-19 07:11:51 +01:00
renovate[bot] db77437898 Update TeslaMate to v1.33.0 (#83)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-29 09:39:46 +00:00
Colin Seymour 111527b4b6 Document migrating to a different PostgresSQL server and restoring teslamate.bak backups (#82)
* Add migrate and restore steps

* Link to TM backup docs

* Fix link and addon
2025-03-21 15:57:14 +00:00
renovate[bot] df2f87600b Update home-assistant/builder action to v2025.03.0 (#81)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-20 14:50:58 +00:00
Colin Seymour 4bc610e9a7 Update config to enable hassio_api access and improve TeslaMate settings message (#79)
* Addon needs hassio API access

* Fix TeslaMate config behaviour
2025-03-19 12:37:16 +00:00
Colin Seymour 48cc287c98 Update devcontainer config (#78) 2025-03-19 11:27:08 +00:00
Colin Seymour 04eb3228bd Use a PostgreSQL version agnostic method of determining if the database exists (#77)
Use PG version agnostic method to find DB
2025-03-19 11:19:28 +00:00
renovate[bot] a5180bcb8e Update docker/login-action action to v3.4.0 (#74)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-14 11:05:24 +00:00
renovate[bot] f2d6fcd514 Update home-assistant/builder action to v2025 (#72)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-25 16:56:17 +00:00
Colin Seymour 540732b90a Document additional TeslaMate configuration changes (#68)
* Add TeslaMate cfg helper message to log

* Add TeslaMate config details
2025-02-02 09:48:15 +00:00
Colin Seymour 33d50cce75 Document and enforce the Grafana datasource name should be TeslaMate (#65)
* Document that the Grafana datasource must be TeslaMate

* Check datasource name TeslaMate is available
2025-01-27 18:21:43 +00:00
renovate[bot] b5ab114562 Update softprops/action-gh-release action to v2.2.1 (#61)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-08 11:00:56 +00:00
renovate[bot] b17ded97f6 Update softprops/action-gh-release action to v2.2.0 (#60) 2024-12-11 07:52:24 +00:00
renovate[bot] 3a9e27e9ac Update TeslaMate to v1.32.0 (#57)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-23 18:23:33 +00:00
renovate[bot] fb4eff6570 Update softprops/action-gh-release action to v2.1.0 (#56) 2024-11-12 08:00:04 +00:00
renovate[bot] d54d94a480 Update softprops/action-gh-release action to v2.0.9 (#55)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-31 20:18:25 +00:00
renovate[bot] a979b4fdf6 Update TeslaMate to v1.31.1 (#54)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-29 18:55:48 +00:00
Colin Seymour 86fe972431 Switch to new S6 service configuration (#40)
* Add new service files

* Remove moved file

* Move files

* Add exec perm

* Update for new services

* Remove xz-utils after use

* Check labels

* Move labels to build

* Typo

* List images

* Fixit

* Add super lint

* Update labels

* Whoops

* Remove superlint

* Add teslamate dependency

* Fix url
2024-10-27 16:43:53 +00:00
renovate[bot]andColin Seymour 3d167fc6ee Update TeslaMate to v1.31.0 (#51)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Colin Seymour <colin@symr.io>
2024-10-27 16:02:40 +00:00
renovate[bot] 0013f5605b Update actions/checkout action to v4.2.2 (#49)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-27 15:56:16 +00:00
Colin Seymour 826c07ace9 Consolidate versions and Renovate config to a single file (#48)
* Consolidate versions and renovate config to a single file

* Remove unneeded lint check

* Add BUILD_FROM
2024-10-27 15:55:21 +00:00
renovate[bot] 7bfa32be3f Update actions/checkout action to v4.2.1 (#47)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-08 08:51:44 +01:00
renovate[bot] b9e37e9f32 Update actions/checkout action to v4.2.0 (#46)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-26 10:14:52 +01:00
Colin Seymour 945a7c4822 Fix release note gathering (#44)
Parse json
2024-09-22 12:27:00 +01:00
Colin Seymour 4567555071 Quote DB name in existance check (#43)
Quote DB name
2024-09-13 09:56:26 +01:00
Colin Seymour 694b43eb93 Only include Teslamate release notes if updated since last release (#38) 2024-08-30 13:10:24 +01:00
Colin Seymour 04e01df081 Make startup scripts robust (#37)
* Clean up file if exists and mkdir -p

* Make importing dashboards more robust

* Update info msgs
2024-08-30 12:45:36 +01:00
renovate[bot] d97377bc0e Update home-assistant/builder action to v2024.08.2 (#30)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-24 09:33:15 +01:00
Colin Seymour 0aca1dd417 Fix image URL (#33) 2024-08-24 09:32:36 +01:00
renovate[bot]andColin Seymour 556a2f23af Update softprops/action-gh-release action to v2.0.8 (#26)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Colin Seymour <colin@symr.io>
2024-08-12 06:56:30 +01:00
Colin Seymour a73430c34d Add optional env vars config option (#29)
* Add optional env vars config option

* Don't use master builder

It's borked - https://github.com/home-assistant/builder/issues/212

* Use specific builder version
2024-08-11 12:22:12 +01:00
Colin Seymour 2b40dd2668 Fix Postgres link (#28)
* Don't default to old Postgres

* Fix postgres link
2024-07-22 14:48:06 +01:00
renovate[bot] 1757ed78ff Update docker/login-action action to v3.3.0 (#27)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-22 14:31:04 +01:00
32 changed files with 1214 additions and 290 deletions
+14
View File
@@ -0,0 +1,14 @@
{
"features": {
"ghcr.io/eitsupi/devcontainer-features/jq-likes:2": {
"version": "2.1.1",
"resolved": "ghcr.io/eitsupi/devcontainer-features/jq-likes@sha256:6a8b044f9f424097ec6d212167836b26994a76a556cbcb202084be79fe8032bd",
"integrity": "sha256:6a8b044f9f424097ec6d212167836b26994a76a556cbcb202084be79fe8032bd"
},
"ghcr.io/jungaretti/features/ripgrep:1": {
"version": "1.0.1",
"resolved": "ghcr.io/jungaretti/features/ripgrep@sha256:c0922b6f4a9184080c8435b6ef25983dee6c734733f0a749d852ad0d33de2253",
"integrity": "sha256:c0922b6f4a9184080c8435b6ef25983dee6c734733f0a749d852ad0d33de2253"
}
}
}
+18 -5
View File
@@ -1,11 +1,20 @@
{
"name": "HA Addon: TeslaMate",
"image": "ghcr.io/home-assistant/devcontainer:addons",
"appPort": ["7123:8123", "7357:4357"],
"postStartCommand": "sudo sed -i 's/export SUPERVISOR_VERSION=.*/export SUPERVISOR_VERSION=2024.03.1.dev1803/' /etc/supervisor_scripts/common && bash devcontainer_bootstrap", // Workaround until https://github.com/home-assistant/supervisor/issues/5021 is fixed"
"image": "ghcr.io/home-assistant/devcontainer:6-apps",
"overrideCommand": false,
"remoteUser": "vscode",
"appPort": ["7123:8123", "7180:80", "7357:4357"],
"postStartCommand": "bash devcontainer_bootstrap",
"runArgs": ["-e", "GIT_EDITOR=code --wait", "--privileged"],
"workspaceFolder": "/mnt/supervisor/addons/local/${localWorkspaceFolderBasename}",
"workspaceMount": "source=${localWorkspaceFolder},target=${containerWorkspaceFolder},type=bind,consistency=cached",
"containerEnv": {
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
"WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}",
"SUPERVISOR_CHANNEL": "stable"
},
"features": {
"ghcr.io/eitsupi/devcontainer-features/jq-likes:2": {},
"ghcr.io/jungaretti/features/ripgrep:1": {}
},
"customizations": {
"vscode": {
@@ -27,5 +36,9 @@
}
}
},
"mounts": ["type=volume,target=/var/lib/docker"]
"mounts": [
"type=volume,target=/var/lib/docker",
"type=volume,target=/var/lib/containerd",
"type=volume,target=/mnt/supervisor",
]
}
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Remove an add-on's registry image before the Supervisor starts so it must
# build the current checkout's Dockerfile.
set -euo pipefail
config="${1:-config.json}"
if [ ! -f "$config" ]; then
echo "ERROR: Add-on config not found: ${config}" >&2
exit 1
fi
if ! jq empty "$config"; then
echo "ERROR: Invalid add-on config: ${config}" >&2
exit 1
fi
if jq -e 'has("image")' "$config" > /dev/null; then
temporary_config=$(mktemp "${config}.XXXXXX")
trap 'rm -f "$temporary_config"' EXIT
jq 'del(.image)' "$config" > "$temporary_config"
mv "$temporary_config" "$config"
fi
if jq -e 'has("image")' "$config" > /dev/null; then
echo "ERROR: Failed to remove image from ${config}" >&2
exit 1
fi
echo "Registry image disabled; the Supervisor will build the current checkout"
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# run-integration-test.sh - Boot the Supervisor and run setup-ha.sh against it.
#
# Usage: bash .devcontainer/run-integration-test.sh [channel]
#
# The channel (stable, beta or dev) selects which Home Assistant release the
# Supervisor installs. It defaults to stable so that CI tests what users actually
# run; the devcontainer image itself defaults to dev, which means unreleased
# Home Assistant changes would otherwise land straight in CI.
#
# Set DEBUG=1 to stream the Supervisor log instead of capturing it.
set -euo pipefail
export SUPERVISOR_CHANNEL="${1:-stable}"
SUPERVISOR_LOG="/tmp/supervisor.log"
bash .devcontainer/prepare-local-addon.sh
# The v6 devcontainer exposes host AppArmor through OS Agent, but profiles
# applied to containers in its nested Docker daemon break the PostgreSQL image.
sudo systemctl stop haos-agent
# supervisor_run hardcodes SUPERVISOR_DEV=1, which makes the Supervisor force its
# update channel to dev and so install nightly Home Assistant builds whatever
# channel is asked for.
sudo sed -i '/-e SUPERVISOR_DEV=1/d' /usr/bin/supervisor_run
# The channel is otherwise only reachable through the Supervisor API, by which
# point it has already installed Home Assistant, so seed it before first boot.
sudo mkdir -p /mnt/supervisor
printf '{"channel": "%s"}\n' "${SUPERVISOR_CHANNEL}" | sudo tee /mnt/supervisor/updater.json > /dev/null
echo "Home Assistant channel: ${SUPERVISOR_CHANNEL}"
if [ -n "${DEBUG:-}" ]; then
supervisor_run 2>&1 | tee "${SUPERVISOR_LOG}" &
else
supervisor_run > "${SUPERVISOR_LOG}" 2>&1 &
fi
if ! bash .devcontainer/setup-ha.sh; then
echo "::group::Supervisor log (last 300 lines, image pull progress omitted)"
grep -v -E "docker_image_pull_update|pull_progress" "${SUPERVISOR_LOG}" | tail -n 300
echo "::endgroup::"
exit 1
fi
+504
View File
@@ -0,0 +1,504 @@
#!/usr/bin/env bash
# setup-ha.sh - Idempotent Home Assistant setup for addon development
#
# This script:
# 1. Completes HA onboarding (creates admin user)
# 2. Adds the alexbelgium addon repository (for PostgreSQL)
# 3. Installs and starts PostgreSQL 17
# 4. Installs, configures, and starts the local addon
#
# Usage: bash .devcontainer/setup-ha.sh
# DEBUG=1 bash .devcontainer/setup-ha.sh # verbose output + error details
#
# Safe to run multiple times (idempotent).
set -euo pipefail
# --- Addon-specific configuration ---
ADDON_NAME="$(jq -r '.name' config.json)"
ADDON_SLUG="local_$(jq -r '.slug' config.json)"
ADDON_PORT="$(jq -r '.ports | keys[0] | split("/") | .[0]' config.json)"
ADDON_READY_LOG="TeslaMateWeb.Endpoint"
TIMEZONE="Europe/London"
# --- Home Assistant configuration ---
HA_HOST="localhost"
# Core binds port 80 under the Supervisor from 2026.8 onwards, and 8123 before
# that, so wait_for_ha resolves the port at runtime rather than assuming one.
HA_PORTS=(80 8123)
HA_URL=""
CLIENT_ID=""
ALEXBELGIUM_REPO="https://github.com/alexbelgium/hassio-addons"
ADMIN_USER="admin"
ADMIN_PASS="pass"
# --- PostgreSQL configuration ---
POSTGRES_PASSWORD="homeassistant"
POSTGRES_USER="postgres"
POSTGRES_DB="${ADDON_NAME,,}"
POSTGRES_PORT=5432
# Addon slug = repo_hash + "_" + addon_slug_from_config
# Repo hash: first 8 chars of sha1("https://github.com/alexbelgium/hassio-addons")
POSTGRES_SLUG="db21ed7f_postgres_latest"
# --- Helpers ---
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m' # No Color
log() { echo -e "${CYAN}==>${NC} ${BOLD}$*${NC}"; }
warn() { echo -e "${YELLOW}WARNING:${NC} $*" >&2; }
err() { echo -e "${RED}ERROR:${NC} $*" >&2; exit 1; }
ok() { echo -e "${CYAN}==>${NC} ${GREEN}$*${NC}"; }
# Parse JSON, aborting with the offending body instead of a bare jq parse error
jq_or_die() {
local json="$1" filter="$2" context="$3"
if [ -z "$json" ] || ! jq empty <<< "$json" 2>/dev/null; then
err "${context}: expected JSON, got: ${json}"
fi
jq -r "$filter" <<< "$json"
}
# Run Supervisor API call via hassio_cli container
# IMPORTANT: Use single quotes for outer sh -c argument so $SUPERVISOR_TOKEN
# is expanded by the inner shell (where the env var exists), not the outer bash.
supervisor_api() {
local method="$1" endpoint="$2"
docker exec hassio_cli sh -c \
'curl -s -X '"${method}"' -H "Authorization: Bearer $SUPERVISOR_TOKEN" -H "Content-Type: application/json" http://supervisor'"${endpoint}"
}
supervisor_api_with_body() {
local method="$1" endpoint="$2" body="$3"
docker exec -e "BODY=${body}" hassio_cli sh -c \
'curl -s -X '"${method}"' -H "Authorization: Bearer $SUPERVISOR_TOKEN" -H "Content-Type: application/json" -d "$BODY" http://supervisor'"${endpoint}"
}
# Query a specific jq field from a Supervisor API response.
# Supervisor reports HTTP errors as plain text (e.g. "404: Not Found"), which is
# not JSON, so surface those and return nothing to let callers keep polling.
supervisor_api_jq() {
local endpoint="$1" jq_filter="$2"
local response
response=$(supervisor_api GET "${endpoint}")
if [ -z "$response" ] || ! jq empty <<< "$response" 2>/dev/null; then
warn "Supervisor API ${endpoint} returned non-JSON: ${response}"
return 0
fi
jq -r "$jq_filter" <<< "$response"
}
# Check if a jq expression matches (returns 0 if match found, 1 otherwise)
supervisor_api_jq_test() {
local endpoint="$1" jq_filter="$2"
docker exec -e "JQ_FILTER=${jq_filter}" hassio_cli sh -c \
'curl -s -H "Authorization: Bearer $SUPERVISOR_TOKEN" http://supervisor'"${endpoint}"' | jq -e "$JQ_FILTER" > /dev/null 2>&1'
}
# --- Step 0: Wait for HA to be ready ---
# Print an addon's log to help diagnose a failure
dump_addon_logs() {
local slug="$1"
echo "::group::${slug} logs"
supervisor_api GET "/addons/${slug}/logs" || true
echo "::endgroup::"
}
# Best-effort Home Assistant Core state from the Supervisor, for diagnostics
ha_core_state() {
local info
info=$(supervisor_api GET "/core/info" 2>/dev/null) || true
if [ -n "$info" ] && jq empty <<< "$info" 2>/dev/null; then
jq -r '.data.state // "unknown"' <<< "$info"
else
echo "unavailable"
fi
}
# HA answers on its HTTP port long before the API is usable, and from 2026.8 the
# port itself moved from 8123 to 80 under the Supervisor. Older releases still use
# 8123, and during onboarding new releases answer on both - but 8123 only serves a
# redirect that Core tears down once onboarding completes. So port 80 is tried
# first, redirects are deliberately not followed, and a port only counts as ready
# when it serves the onboarding payload itself rather than merely valid JSON.
HA_PROBE=""
ha_probe() {
local port="$1" raw status body
raw=$(curl -s --max-time 5 -w $'\n%{http_code}' "http://${HA_HOST}:${port}/api/onboarding" 2>/dev/null || true)
status=${raw##*$'\n'}
body=${raw%$'\n'*}
HA_PROBE="${HA_PROBE:+${HA_PROBE} }port=${port} http=${status:-none} body=${body:0:80}"
jq -e 'type == "array" and (.[0] | has("step"))' <<< "$body" > /dev/null 2>&1
}
# Sets HA_URL and CLIENT_ID on success. Must not run in a subshell.
ha_resolve_url() {
local port
HA_PROBE=""
for port in "${HA_PORTS[@]}"; do
if ha_probe "$port"; then
HA_URL="http://${HA_HOST}:${port}"
CLIENT_ID="${HA_URL}/"
return 0
fi
done
return 1
}
wait_for_ha() {
log "Waiting for Home Assistant to be ready..."
local max_attempts=120
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
if ha_resolve_url; then
ok "Home Assistant is ready at ${HA_URL}"
return 0
fi
if [ $((attempt % 6)) -eq 0 ]; then
log " attempt ${attempt}/${max_attempts}: core=$(ha_core_state) ${HA_PROBE}"
fi
sleep 5
attempt=$((attempt + 1))
done
err "Home Assistant did not become ready in time (core=$(ha_core_state) ${HA_PROBE})"
}
# --- Step 1: Complete onboarding ---
complete_onboarding() {
log "Checking onboarding status..."
local onboarding
onboarding=$(curl -sfL "${HA_URL}/api/onboarding")
local user_done
user_done=$(jq_or_die "$onboarding" '.[] | select(.step == "user") | .done' "Onboarding status")
if [ "$user_done" = "true" ]; then
ok "Onboarding already completed"
return 0
fi
log "Creating admin user '${ADMIN_USER}'..."
local auth_response
auth_response=$(curl -sfL -X POST "${HA_URL}/api/onboarding/users" \
-H "Content-Type: application/json" \
-d "{\"client_id\":\"${CLIENT_ID}\",\"name\":\"Admin\",\"username\":\"${ADMIN_USER}\",\"password\":\"${ADMIN_PASS}\",\"language\":\"en\"}")
local auth_code
auth_code=$(jq_or_die "$auth_response" '.auth_code' "Create admin user")
if [ -z "$auth_code" ] || [ "$auth_code" = "null" ]; then
err "Failed to create user: ${auth_response}"
fi
# Exchange auth code for access token
local token_response
token_response=$(curl -sfL -X POST "${HA_URL}/auth/token" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "code=${auth_code}" \
--data-urlencode "client_id=${CLIENT_ID}")
local access_token
access_token=$(jq_or_die "$token_response" '.access_token' "Token exchange")
if [ -z "$access_token" ] || [ "$access_token" = "null" ]; then
err "Failed to get access token: ${token_response}"
fi
# Complete remaining onboarding steps
log "Completing onboarding steps..."
curl -sfL -X POST "${HA_URL}/api/onboarding/core_config" \
-H "Authorization: Bearer ${access_token}" \
-H "Content-Type: application/json" \
-d "{}" > /dev/null 2>&1 || true
curl -sfL -X POST "${HA_URL}/api/onboarding/analytics" \
-H "Authorization: Bearer ${access_token}" \
-H "Content-Type: application/json" \
-d "{}" > /dev/null 2>&1 || true
curl -sfL -X POST "${HA_URL}/api/onboarding/integration" \
-H "Authorization: Bearer ${access_token}" \
-H "Content-Type: application/json" \
-d "{\"client_id\":\"${CLIENT_ID}\",\"redirect_uri\":\"${HA_URL}/\"}" > /dev/null 2>&1 || true
ok "Onboarding complete"
}
# --- Step 2: Add alexbelgium addon repository ---
add_addon_repo() {
log "Checking addon repositories..."
if supervisor_api_jq_test "/store/repositories" ".data[] | select(.source == \"${ALEXBELGIUM_REPO}\")"; then
ok "Repository already added: ${ALEXBELGIUM_REPO}"
return 0
fi
log "Adding addon repository: ${ALEXBELGIUM_REPO}"
supervisor_api_with_body POST "/store/repositories" \
"{\"repository\":\"${ALEXBELGIUM_REPO}\"}" > /dev/null
# Wait for the store to refresh by checking if the postgres addon is available
log "Waiting for store to refresh..."
local max_attempts=60
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
if supervisor_api_jq_test "/addons/${POSTGRES_SLUG}/info" '.data.name'; then
ok "Store refreshed successfully"
return 0
fi
sleep 5
attempt=$((attempt + 1))
done
err "Store did not refresh in time - postgres addon not found"
}
# --- Step 3: Install PostgreSQL ---
install_postgres() {
local postgres_slug="$1"
log "Checking PostgreSQL addon status (${postgres_slug})..."
local version
version=$(supervisor_api_jq "/addons/${postgres_slug}/info" '.data.version // empty')
if [ -n "$version" ]; then
local state
state=$(supervisor_api_jq "/addons/${postgres_slug}/info" '.data.state')
ok "PostgreSQL addon already installed (version: ${version}, state: ${state})"
return 0
fi
log "Installing PostgreSQL addon..."
supervisor_api POST "/addons/${postgres_slug}/install" > /dev/null
# Wait for installation to complete
log "Waiting for PostgreSQL installation..."
local max_attempts=120
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
version=$(supervisor_api_jq "/addons/${postgres_slug}/info" '.data.version // empty')
if [ -n "$version" ]; then
ok "PostgreSQL addon installed (version: ${version})"
return 0
fi
sleep 5
attempt=$((attempt + 1))
done
err "PostgreSQL addon installation timed out"
}
# --- Step 4: Configure and start PostgreSQL ---
configure_and_start_postgres() {
local postgres_slug="$1"
log "Configuring PostgreSQL addon..."
local options_json
options_json=$(jq -n \
--arg pw "$POSTGRES_PASSWORD" \
--arg db "$POSTGRES_DB" \
'{options: {POSTGRES_PASSWORD: $pw, POSTGRES_DB: $db, env_vars: []}}')
supervisor_api_with_body POST "/addons/${postgres_slug}/options" \
"${options_json}" > /dev/null
# Check if already running
local state
state=$(supervisor_api_jq "/addons/${postgres_slug}/info" '.data.state')
if [ "$state" = "started" ]; then
ok "PostgreSQL addon already running"
return 0
fi
log "Starting PostgreSQL addon..."
supervisor_api POST "/addons/${postgres_slug}/start" > /dev/null
# Wait for postgres to be ready
log "Waiting for PostgreSQL to be ready..."
local max_attempts=60
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
state=$(supervisor_api_jq "/addons/${postgres_slug}/info" '.data.state')
if [ "$state" = "started" ]; then
ok "PostgreSQL addon is running"
wait_for_postgres_ready "$postgres_slug"
return 0
fi
sleep 5
attempt=$((attempt + 1))
done
err "PostgreSQL addon did not start in time"
}
wait_for_postgres_ready() {
local postgres_slug="$1"
# Supervisor renamed add-on containers from addon_* to app_*, so try both.
local containers=("app_${postgres_slug}" "addon_${postgres_slug}")
log "Waiting for PostgreSQL database to accept connections..."
local max_attempts=60
local attempt=0
local container probe
while [ "$attempt" -lt "$max_attempts" ]; do
for container in "${containers[@]}"; do
if probe=$(docker exec -e "PGPASSWORD=${POSTGRES_PASSWORD}" "$container" \
psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT 1" 2>&1); then
ok "PostgreSQL database is ready"
return 0
fi
done
sleep 5
attempt=$((attempt + 1))
done
warn "Last psql output: ${probe}"
dump_addon_logs "$postgres_slug"
err "PostgreSQL database did not become ready in time"
}
# --- Step 5: Install and configure addon ---
install_addon() {
log "Checking ${ADDON_NAME} addon status..."
local version
version=$(supervisor_api_jq "/addons/${ADDON_SLUG}/info" '.data.version // empty')
if [ -n "$version" ]; then
local state
state=$(supervisor_api_jq "/addons/${ADDON_SLUG}/info" '.data.state')
ok "${ADDON_NAME} addon already installed (version: ${version}, state: ${state})"
return 0
fi
log "Installing ${ADDON_NAME} addon..."
supervisor_api POST "/addons/${ADDON_SLUG}/install" > /dev/null
# Wait for installation to complete (the TeslaMate image is large)
log "Waiting for ${ADDON_NAME} installation..."
local max_attempts=120
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
version=$(supervisor_api_jq "/addons/${ADDON_SLUG}/info" '.data.version // empty')
if [ -n "$version" ]; then
ok "${ADDON_NAME} addon installed (version: ${version})"
return 0
fi
sleep 5
attempt=$((attempt + 1))
done
err "${ADDON_NAME} addon installation timed out"
}
configure_and_start_addon() {
local postgres_slug="$1"
# Derive the postgres hostname from its slug (underscores → hyphens)
local postgres_host
postgres_host=$(echo "$postgres_slug" | tr '_' '-')
log "Configuring ${ADDON_NAME} addon (db host: ${postgres_host})..."
local current_options overrides options_json
current_options=$(supervisor_api_jq "/addons/${ADDON_SLUG}/info" '.data.options')
overrides=$(jq -n \
--arg user "$POSTGRES_USER" \
--arg pass "$POSTGRES_PASSWORD" \
--argjson port "$POSTGRES_PORT" \
--arg host "$postgres_host" \
--arg db "$POSTGRES_DB" \
--arg tz "$TIMEZONE" \
'{database_user: $user, database_pass: $pass, database_port: $port, database_host: $host, database_name: $db, database_ssl: false, disable_mqtt: true, grafana_import_dashboards: false, timezone: $tz}')
options_json=$(jq -n --argjson cur "$current_options" --argjson ovr "$overrides" '{options: ($cur + $ovr)}')
supervisor_api_with_body POST "/addons/${ADDON_SLUG}/options" \
"${options_json}" > /dev/null
# Check if already running
local state
state=$(supervisor_api_jq "/addons/${ADDON_SLUG}/info" '.data.state')
if [ "$state" = "started" ]; then
ok "${ADDON_NAME} addon already running"
wait_for_addon_ready
return 0
fi
log "Starting ${ADDON_NAME} addon..."
supervisor_api POST "/addons/${ADDON_SLUG}/start" > /dev/null
# Wait for addon state to become "started"
log "Waiting for ${ADDON_NAME} addon to start..."
local max_attempts=60
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
state=$(supervisor_api_jq "/addons/${ADDON_SLUG}/info" '.data.state')
if [ "$state" = "started" ]; then
break
fi
sleep 5
attempt=$((attempt + 1))
done
if [ "$state" != "started" ]; then
dump_addon_logs "${ADDON_SLUG}"
err "${ADDON_NAME} addon did not start in time (state: ${state})"
fi
wait_for_addon_ready
ok "${ADDON_NAME} addon started and ready"
}
wait_for_addon_ready() {
log "Waiting for ${ADDON_NAME} to be ready (listening on port ${ADDON_PORT})..."
local max_attempts=60
local attempt=0
while [ "$attempt" -lt "$max_attempts" ]; do
local logs
logs=$(supervisor_api GET "/addons/${ADDON_SLUG}/logs" 2>/dev/null || true)
if echo "$logs" | grep -qF "$ADDON_READY_LOG"; then
ok "${ADDON_NAME} is ready"
return 0
fi
sleep 5
attempt=$((attempt + 1))
done
dump_addon_logs "${ADDON_SLUG}"
err "${ADDON_NAME} did not become ready in time (never saw '${ADDON_READY_LOG}' in logs)"
}
# --- Main ---
main() {
log "Setting up Home Assistant for ${ADDON_NAME} development"
echo ""
wait_for_ha
complete_onboarding
add_addon_repo
install_postgres "$POSTGRES_SLUG"
configure_and_start_postgres "$POSTGRES_SLUG"
install_addon
configure_and_start_addon "$POSTGRES_SLUG"
echo ""
ok "Setup complete!"
local host_port
# Mirrors the appPort list in devcontainer.json.
case "${HA_URL##*:}" in
80) host_port=7180 ;;
*) host_port=7123 ;;
esac
log " Home Assistant: ${HA_URL} (user: ${ADMIN_USER}, pass: ${ADMIN_PASS})"
log " from the host: http://localhost:${host_port}"
log " PostgreSQL: ${POSTGRES_SLUG} (user: ${POSTGRES_USER}, db: ${POSTGRES_DB})"
log " ${ADDON_NAME}: accessible via Home Assistant ingress"
}
main "$@"
+5 -4
View File
@@ -2,19 +2,20 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":disableDependencyDashboard"
":disableDependencyDashboard",
"helpers:pinGitHubActionDigests"
],
"prHourlyLimit": 4,
"prBodyTemplate": "{{{table}}}{{{warnings}}}{{{notes}}}{{{changelogs}}}",
"semanticCommits": "disabled",
"commitMessageTopic": "{{#if (containsString depName 'teslamate')}}TeslaMate{{else}}{{depName}}{{/if}}",
"enabledManagers": ["custom.regex", "github-actions", "dockerfile"],
"enabledManagers": ["custom.regex", "github-actions"],
"customManagers": [
{
"customType": "regex",
"fileMatch": "^build.json$",
"fileMatch": "(^|/)Dockerfile$",
"matchStrings": [
"\"teslamate_version\": \"(?<currentValue>\\d+\\.\\d+\\.\\d+)\""
"ARG teslamate_version=(?<currentValue>\\d+\\.\\d+\\.\\d+)"
],
"datasourceTemplate": "github-releases",
"depNameTemplate": "teslamate-org/teslamate"
+58
View File
@@ -0,0 +1,58 @@
name: Integration Test
on:
push:
branches:
- main
paths:
- config.json
- Dockerfile
- rootfs/**
- .devcontainer/**
pull_request:
branches:
- main
paths:
- config.json
- Dockerfile
- rootfs/**
- .devcontainer/**
# Home Assistant and the third-party add-on repository this test installs both
# move independently of this repo, so the test can break with no change here.
# Run it on a schedule to catch that on main rather than on an unrelated pull
# request that happens to touch one of the paths above.
schedule:
- cron: "17 6 * * *"
workflow_dispatch:
jobs:
integration:
name: Integration test
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Run integration test in devcontainer
uses: devcontainers/ci@513af61f4de4f75d37e4438f184ba4358f0fc1ca # v0.3.1900000450
with:
runCmd: bash .devcontainer/run-integration-test.sh stable
# Early warning for Home Assistant changes that have not reached stable yet.
# Deliberately never runs on a pull request, so a broken beta cannot block a
# merge, and is non-blocking when it does run.
canary:
name: Integration test (beta)
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 30
continue-on-error: true
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Run integration test in devcontainer
uses: devcontainers/ci@513af61f4de4f75d37e4438f184ba4358f0fc1ca # v0.3.1900000450
with:
runCmd: bash .devcontainer/run-integration-test.sh beta
+2 -12
View File
@@ -14,19 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: ⤵️ Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: 🚀 Run Home Assistant Add-on Lint
uses: frenck/action-addon-linter@v2
uses: frenck/action-addon-linter@b9cfd1bc62ba60d2c4fc96317b5f7ada5923f4a7 # v2.21.1
with:
path: "./"
- name: Check versions match
run: |
docker_ver=$(grep "FROM teslamate/teslamate" Dockerfile | cut -d':' -f2)
build_ver=$(jq -r ".args.teslamate_version" build.json)
if [ "$docker_ver" != "$build_ver" ]; then
echo -e "\033[31;1mTeslaMate versions don't match: Dockerfile => ${docker_ver} | build.json => ${build_ver}\033[0m"
exit 1
fi
echo -e "\033[32;1mTeslaMate versions match: Dockerfile => ${docker_ver} | build.json => ${build_ver}\033[0m"
+62 -27
View File
@@ -11,41 +11,76 @@ on:
type: string
jobs:
publish:
name: Publish
init:
name: Initialize build
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: ${{ steps.version.outputs.version }}
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- name: Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Get version
id: version
run: |
version="${{ inputs.version }}"
if [ -n "$version" ]; then
echo "version=${version}" >> "$GITHUB_OUTPUT"
else
echo "version=$(curl --header "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" --silent "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest" | jq -r .tag_name | sed s/v//)" >> "$GITHUB_OUTPUT"
fi
- name: Build matrix
id: matrix
run: |
# Derive the per-arch build matrix from config.json. amd64 and aarch64
# build on native runners (no QEMU). Base image, build args and labels
# all live in the Dockerfile, so nothing extra is needed here.
image_template=$(jq -r '.image' config.json)
architectures=$(jq -r '.arch[]' config.json)
matrix='{"include":[]}'
for arch in $architectures; do
case "${arch}" in
amd64) os="ubuntu-24.04" ;;
aarch64) os="ubuntu-24.04-arm" ;;
*) echo "::error::Unsupported architecture: ${arch}"; exit 1 ;;
esac
image="${image_template//\{arch\}/${arch}}"
matrix=$(jq -c \
--arg arch "$arch" \
--arg os "$os" \
--arg image "$image" \
'.include += [{arch: $arch, os: $os, image: $image}]' <<< "${matrix}")
done
echo "matrix=${matrix}" >> "$GITHUB_OUTPUT"
build:
name: Build ${{ matrix.arch }} image
needs: init
runs-on: ${{ matrix.os }}
permissions:
contents: read
packages: write
strategy:
matrix:
arch: ["amd64", "armv7", "aarch64"]
fail-fast: false
matrix: ${{ fromJSON(needs.init.outputs.matrix) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4.1.7
- name: Get version
run: |
version=${{ inputs.version }}
if [ -n "$version" ]; then
echo "latest_release=$version" >> $GITHUB_ENV
else
echo "latest_release=$(curl --header "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" --silent https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest | jq -r .tag_name | sed s/v// )" >> $GITHUB_ENV
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@v3.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build and publish
uses: home-assistant/builder@master
uses: home-assistant/builder/actions/build-image@4de35182ce1e329181bffcbcc84d33db5e2c7e10 # 2026.06.0
with:
args: |
--${{ matrix.arch }} \
--version ${{ env.latest_release }} \
--target /data/ \
--addon
arch: ${{ matrix.arch }}
image: ${{ matrix.image }}
image-tags: |
${{ needs.init.outputs.version }}
latest
container-registry-password: ${{ secrets.GITHUB_TOKEN }}
push: "true"
cosign: "false"
version: ${{ needs.init.outputs.version }}
+7 -6
View File
@@ -19,7 +19,7 @@ jobs:
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: teslamate
token: ${{ secrets.GITHUB_TOKEN }}
@@ -28,19 +28,20 @@ jobs:
echo "latest_release=$(curl --header "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" --silent https://api.github.com/repos/$GITHUB_REPOSITORY/releases/latest | jq -r .tag_name | sed s/v// )" >> $GITHUB_ENV
- name: Increment semver
id: semver
uses: matt-FFFFFF/simple-semver@v0.1.1
uses: matt-FFFFFF/simple-semver@967d66a65b0c5afae2a815ab2d311f66a32293ab # v0.1.1
with:
semver-input: ${{ env.latest_release }}
increment: ${{ github.event.inputs.semverIncrement }}
- name: Get TeslaMate release notes
run: |
touch CHANGELOG.txt
gh pr list --repo lildude/ha-addon-teslamate --state=merged --search="Update TeslaMate" --limit=1 --json=body --jq '.[].body' | sed -n "/<\/summary>/,/<\/details>/p" | sed '1d;$d' > changelog.tmp
last_release_date=$(gh release list --repo lildude/ha-addon-teslamate --limit=1 --json=publishedAt | jq -r '.[].publishedAt')
gh pr list --repo lildude/ha-addon-teslamate --state=merged --search="Update TeslaMate in:title closed:>$last_release_date" --limit=1 --json=body --jq '.[].body' | sed -n "/<\/summary>/,/<\/details>/p" | sed '1d;$d' > changelog.tmp
if [ -s changelog.tmp ]; then
echo "## TeslaMate Release Notes" > CHANGELOG.txt
cat changelog.tmp >> CHANGELOG.txt
echo -e "---\n\n## Add-on Release Notes\n\n" >> CHANGELOG.txt
fi
echo -e "---\n\n## Add-on Release Notes\n\n" >> CHANGELOG.txt
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create tag
@@ -51,7 +52,7 @@ jobs:
git push --tags
working-directory: ${{ github.workspace }}/teslamate
- name: Create release
uses: softprops/action-gh-release@v2.0.7
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
tag_name: v${{ steps.semver.outputs.semver }}
body_path: CHANGELOG.txt
@@ -60,7 +61,7 @@ jobs:
prerelease: false
token: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout addon repo
uses: actions/checkout@v4.1.7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: repo
repository: lildude/ha-addons
+55 -17
View File
@@ -5,9 +5,6 @@ on:
branches:
- main
paths:
- build.yaml
- config.yaml
- build.json
- config.json
- Dockerfile
- rootfs/**
@@ -15,30 +12,71 @@ on:
branches:
- main
paths:
- build.yaml
- config.yaml
- build.json
- config.json
- Dockerfile
- rootfs/**
jobs:
build:
init:
name: Initialize build
runs-on: ubuntu-latest
name: Build add-on
permissions:
contents: read
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build matrix
id: matrix
run: |
# Derive the per-arch build matrix from config.json. amd64 and aarch64
# build on native runners (no QEMU). Base image, build args and labels
# all live in the Dockerfile, so nothing extra is needed here.
image_template=$(jq -r '.image' config.json)
architectures=$(jq -r '.arch[]' config.json)
matrix='{"include":[]}'
for arch in $architectures; do
case "${arch}" in
amd64) os="ubuntu-24.04" ;;
aarch64) os="ubuntu-24.04-arm" ;;
*) echo "::error::Unsupported architecture: ${arch}"; exit 1 ;;
esac
image="${image_template//\{arch\}/${arch}}"
matrix=$(jq -c \
--arg arch "$arch" \
--arg os "$os" \
--arg image "$image" \
'.include += [{arch: $arch, os: $os, image: $image}]' <<< "${matrix}")
done
echo "matrix=${matrix}" >> "$GITHUB_OUTPUT"
build:
name: Build ${{ matrix.arch }} add-on
needs: init
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
matrix:
arch: ["amd64", "armv7", "aarch64"]
fail-fast: false
matrix: ${{ fromJSON(needs.init.outputs.matrix) }}
steps:
- name: Check out repository
uses: actions/checkout@v4.1.7
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build add-on
uses: home-assistant/builder@master
uses: home-assistant/builder/actions/build-image@4de35182ce1e329181bffcbcc84d33db5e2c7e10 # 2026.06.0
with:
args: |
--test \
--${{ matrix.arch }} \
--target /data/ \
--addon
arch: ${{ matrix.arch }}
image: ${{ matrix.image }}
image-tags: test
container-registry-password: ${{ secrets.GITHUB_TOKEN }}
load: "true"
cosign: "false"
version: test
- name: Image labels
run: |
docker inspect ${{ matrix.image }}:test --format='{{json .Config.Labels}}' | jq
-6
View File
@@ -1,11 +1,5 @@
{
"json.schemas": [
{
"fileMatch": [
"**/build.json"
],
"url": "https://raw.githubusercontent.com/frenck/action-addon-linter/main/src/build.schema.json"
},
{
"fileMatch": [
"**/config.json"
+120 -4
View File
@@ -7,7 +7,7 @@ A self-hosted data logger for your Tesla 🚘
[TeslaMate][teslamate] is a powerful, self-hosted data logger for your Tesla.
- Written in **[Elixir][elixir]**
- Data is stored in a **Postgres** database
- Data is stored in a **PostgreSQL** database
- Visualization and data analysis with **Grafana**
- Vehicle data is published to a local **MQTT** Broker
@@ -16,6 +16,7 @@ This add-on allows you to run [TeslaMate][teslamate] on your Home Assistant serv
## Configuration
The configuration is self-explanatory, but essentially we need details about accessing the PostgreSQL database, Grafana and optionally MQTT.
You will also need to configure TeslaMate (see "TeslaMate Configuration" below) so the links to TeslaMate in the Grafana dashboards work.
### Options
@@ -76,7 +77,6 @@ custom_plugins:
- name: panodata-map-panel
url: https://github.com/panodata/panodata-map-panel/releases/download/0.16.0/panodata-map-panel-0.16.0.zip
unsigned: true
custom_plugins: []
env_vars:
- name: GF_SECURITY_ADMIN_USER
value: <youruser>
@@ -105,7 +105,9 @@ _Note_ that the security admin password and usernames can only be set on the fir
Once you have Grafana up and running, you'll need to configure a data source to read data from the PostgreSQL database:
![Grafana Postgres data source][grafana-datasource]
‼️ **Note**: The name must be `TeslaMate` as all the dashboards expect the datasource to use this name.
![Grafana PostgreSQL data source][grafana-datasource]
#### Other Options
@@ -115,6 +117,105 @@ Once you have Grafana up and running, you'll need to configure a data source to
- `import_dir`: (Optional) The path to the directory where TeslaMate should look for the TeslaMate export CSV file. Default: `/share/teslamate`.
- `env_vars`: (Optional) Set additional environment variables for TeslaMate which aren't currently exposed by the configuration options in this add-on.
Each entry is made up of a name and value:
- `name`: The case-sensitive environment variable name.
- `value`: The value to be set in the environment variable.
Note: These will also overwrite any environment variable set using the configuration options above.
## TeslaMate Configuration
In order for the links in the Grafana dashboards and the TeslaMate UI to link to the correct locations, you will also need to configure TeslaMate.
Once you have started the add-on, look at the log output for the section which looks like this and take note of the two lines with `=>`:
```
[09:26:32] INFO: Configure TeslaMate settings by adding these values
to the URL you use to access your Home Assistant instance:
=> Web App: /api/hassio_ingress/ljiIc6sOVGOSQfTRjscvLVJJS5Rxp33gsdEtf9y3oQY
=> Dashboards: /api/hassio_ingress/G9ocwA44wt9Bcba8LvP8tDhlfFiFnnPftOZBwp-Pgzs
```
From the add-on Info tab, click "Open Web UI", enter the Tesla API access and refresh tokens if you haven't already, and then go to TeslaMate's Settings.
Scroll to the very bottom and set each of the URLs to the URL you use to access your Home Assistant instance with each of the above appended.
For example, if you access your Home Assistant instance at `https://ha.example.com`, set each as follows:
- Web App: `https://ha.example.com/api/hassio_ingress/ljiIc6sOVGOSQfTRjscvLVJJS5Rxp33gsdEtf9y3oQY`
- Dashboards: `https://ha.example.com/api/hassio_ingress/G9ocwA44wt9Bcba8LvP8tDhlfFiFnnPftOZBwp-Pgzs`
Do _not_ use these values. Use the values from your log output.
**Note:** If you do not see the links in your add-on logs, clear the values in the TeslaMate configuration and restart the add-on.
## Migrate TeslaMate to a Different PostgreSQL Server
In order to migrate TeslaMate to a different PostgreSQL server, you will need to make a backup of the original data and then restore it to the destination server.
This isn't much of a challenge for the seasoned sysadmin familiar with Docker, PostgreSQL and the command line, however most Home Assistant users aren't expert sysadmins so this guide if for you.
1. Ensure you have both the current and new PostgreSQL servers installed and running and ensure you have the same users and roles configured on both.
2. Stop the TeslaMate add-on.
3. Install the [pgAdmin4](https://github.com/expaso/hassos-addons) add-on.
4. Start the pgAdmin4 add-on and open the web UI
5. Make a new connection to the current PostgreSQL server by clicking "Add New Server".
1. Give it a recognisable name in the General tab.
2. Switch to the Connection tab and enter the hostname, port, username and password currently used.
You can find these in your TeslaMate add-on configuration.
3. Click Save
6. Click the dropdown next to the new server -> Databases and then select the database you use for TeslaMate.
This will be the same name you configured in the TeslaMate add-on configuration.
7. Go to Tools -> Backup, enter a filename, if you know what you're doing feel free to make other customisations but it's not essential, and then click Backup. [^1]
8. Once the backup has finished, repeats step 5 for your new PostgreSQL server to establish a connection to your new PostgreSQL server.
9. Select the new server, if not already selected, and go to Object -> Create -> Database and enter the same name and owner as used on your old server and click Save.
10. If the database is not already selected, select it and then go to Tools -> Restore, enter the filename you entered in step 7 and click Restore.
You may need to use the file browser to locate the file. If you didn't change the path in step 7, the backup should be in `/root/`.
11. Once the restore has completed, go to the TeslaMate add-on settings and update the database settings to point to your new database server.
The easiest approach is to use the steps in [Migrating to this version of the add-on][migrate-addon] further down in this file and change the `database_host` to the name of your new PostgreSQL server.
12. Update the Grafana configuration by going to the Grafana add-on -> Open web UI -> Grafana logo in top left -> Connections -> Datasources, select the TeslaMate source and change the Host URL to your new PostgreSQL server and click "Save & test".
13. Start the TeslaMate add-on and verifying everything is working as it was before. [^2]
You can now uninstall the pgAdmin4 add-on if you want and stop and uninstall your old PostgreSQL server if you're not using it for anything else.
## Import TeslaMate Backup
If you have made a backup of a TeslaMate installation as per the [TeslaMate backup documentation][teslamate-backup] and now have a `teslamate.bak` file you want to use with this add-on, you can do so as follows:
1. Install this TeslaMate add-on if you haven't already, but don't start it.
2. Install a PostgreSQL server and start it if you haven't already.
We recommend the [PostgreSQL 17 add-on][alexbelgium-postgres] from @alexbelgium's repository, or [TimescaleDB][timescaledb] (for advanced users).
3. Perform steps 3, 4 and 5 from [Migrate TeslaMate to a Different PostreSQL Server][migrate-psql] above.
4. Transfer the `teslamate.bck` file to the `/share/teslamate` folder on your Home Assistant instance.
You can do this using the [Samba][samba-addon] or [SSH][ssh-addon] add-ons.
5. Perform steps 9 and 10 from [Migrate TeslaMate to a Different PostreSQL Server][migrate-psql] above.
Note: the file location will be `/share/teslamate/teslamate.bck`.
6. Configure TeslaMate and Grafana as per the details at the top of this file.
7. Start TeslaMate.
## Data Import from TeslaFi
It is now possible to import CSV data from TeslaFi, refer to the [official docs][teslafi-import].
@@ -132,12 +233,27 @@ Follow this process:
5. Once imported sucessfully, delete the CSV files to avoid the import screen being presented.
[alexbelgium-postgres]: https://github.com/alexbelgium/hassio-addons/tree/master/postgres_17
[docker]: https://hub.docker.com/r/teslamate/teslamate
[elixir]: https://elixir-lang.org/
[grafana-addon]: https://github.com/hassio-addons/addon-grafana
[grafana-datasource]: https://raw.githubusercontent.com/lildude/hassio-addon-teslamate/main/imgs/grafana-postgres.png
[grafana-datasource]: https://raw.githubusercontent.com/lildude/ha-addon-teslamate/main/imgs/grafana-postgres.png
[migrate-psql]: #migrate-teslamate-to-a-different-postresql-server
[migrate-addon]: https://github.com/lildude/ha-addon-teslamate/blob/main/README.md#migrating-to-this-version-of-the-add-on
[mosquitto-docs]: https://github.com/home-assistant/addons/blob/master/mosquitto/DOCS.md
[pgadmin4]: https://github.com/expaso/hassos-addons
[samba-addon]: https://github.com/home-assistant/addons/blob/master/samba/DOCS.md
[ssh-addon]: https://github.com/home-assistant/addons/blob/master/ssh/DOCS.md
[teslafi-import]: https://docs.teslamate.org/docs/import/teslafi
[teslamate]: https://github.com/teslamate-org/teslamate/
[teslamate-backup]: https://docs.teslamate.org/docs/maintenance/backup_restore/#backup
[timescaledb]: https://github.com/expaso/hassos-addon-timescaledb
[^1]:
If the backup or restore fails due to a `server version mismatch` error, you will need to select the latest version of PostgreSQL in pgAdmin4 by going to File -> Paths -> Binary paths and add the correct path for each version.
For example, for PostgreSQL 14, enter `/usr/local/pgsql-14`, for PostgreSQL 15, enter `/usr/local/pgsql-15` etc.
[^2]:
If you see the `ERROR: type "earth" does not exist at character` error in the TeslaMate log, you can fix this by going back into pgAdmin4 and right click on the TeslaMate database -> "CREATE script".
Delete everything auto generated and paste in the [codeblock here](https://github.com/diogob/activerecord-postgres-earthdistance/issues/30#issuecomment-2122829036).
Click "Execute script" (It's the ▶️ symbol at the top).
+17 -27
View File
@@ -1,8 +1,9 @@
FROM teslamate/grafana:1.30.1 as grafana
ARG teslamate_version=4.1.1
FROM teslamate/grafana:${teslamate_version} AS grafana
#---
FROM teslamate/teslamate:1.30.1
FROM teslamate/teslamate:${teslamate_version}
ARG BUILD_ARCH
ARG BASHIO_VERSION=0.16.2
@@ -22,7 +23,6 @@ RUN \
set -x \
&& apt-get update && apt-get install -y --no-install-recommends \
bash \
bind9utils \
ca-certificates \
curl \
jq \
@@ -30,41 +30,31 @@ RUN \
postgresql-client \
tzdata \
xz-utils \
&& rm -rf /var/lib/apt/lists/* \
&& S6_ARCH="${BUILD_ARCH}" \
&& if [ "${BUILD_ARCH}" = "amd64" ]; then S6_ARCH="x86_64"; \
elif [ "${BUILD_ARCH}" = "armv7" ]; then S6_ARCH="arm"; fi \
&& if [ "${BUILD_ARCH}" = "amd64" ]; then S6_ARCH="x86_64"; fi \
&& curl -Ls "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" | tar xpJ -C / \
&& curl -Ls "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz" | tar xpJ -C / \
&& curl -Ls "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-symlinks-noarch.tar.xz" | tar Jxp -C / \
&& curl -Ls "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-symlinks-arch.tar.xz" | tar Jxp -C / \
&& mkdir -p /etc/fix-attrs.d \
&& mkdir -p /etc/services.d \
&& mkdir -p /tmp/bashio \
&& curl -Ls "https://github.com/hassio-addons/bashio/archive/v${BASHIO_VERSION}.tar.gz" | tar xz --strip 1 -C /tmp/bashio \
&& mv /tmp/bashio/lib /usr/lib/bashio \
&& apt purge -y xz-utils \
&& ln -s /usr/lib/bashio/bashio /usr/bin/bashio \
&& rm -rf /tmp/bashio
&& rm -rf /var/lib/apt/lists/* /tmp/*
COPY --chown=root scripts/*.sh /
RUN chmod a+x /*.sh
COPY rootfs /
COPY --from=grafana /dashboards /dashboards
COPY --from=grafana /dashboards_internal /dashboards
COPY --chown=root services/teslamate/run services/teslamate/finish /etc/services.d/teslamate/
RUN chmod a+x /etc/services.d/teslamate/*
COPY --chown=root services/nginx/run services/nginx/finish /etc/services.d/nginx/
RUN chmod a+x /etc/services.d/nginx/*
COPY --chown=root services/nginx/teslamate.conf /etc/nginx/conf.d/
COPY --from=grafana --chown=root /dashboards /dashboards
COPY --from=grafana --chown=root /dashboards_internal /dashboards
LABEL \
maintainer="Colin Seymour (https://github.com/lildude)" \
org.opencontainers.image.authors="Colin Seymour (https://github.com/lildude)" \
org.opencontainers.image.description="A self-hosted data logger for your Tesla." \
org.opencontainers.image.documentation="https://github.com/lildude/ha-addon-teslamate/blob/main/DOCS.md" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.source="https://github.com/lildude/ha-addon-teslamate/" \
org.opencontainers.image.title="Home Assistant Add-on: TeslaMate"
# S6-Overlay
ENTRYPOINT ["/init"]
LABEL \
org.opencontainers.image.title="Home Assistant Add-on: TeslaMate" \
org.opencontainers.image.description="A self-hosted data logger for your Tesla." \
org.opencontainers.image.source="https://github.com/lildude/ha-addon-ghostfolio/" \
org.opencontainers.image.licenses="MIT"
+14 -5
View File
@@ -7,7 +7,7 @@ A self-hosted data logger for your Tesla 🚘
[TeslaMate][teslamate] is a powerful, self-hosted data logger for your Tesla.
- Written in **[Elixir][elixir]**
- Data is stored in a **Postgres** database
- Data is stored in a **PostgreSQL** database
- Visualization and data analysis with **Grafana**
- Vehicle data is published to a local **MQTT** Broker
@@ -24,8 +24,8 @@ Thanks to @matt-FFFFFF for maintaining this add-on in the past.
## Requirements
TeslaMate needs a PostgreSQL database.
I recommend using the [PostgreSQL add-on][alexbelgium-postgres] from @alexbelgium's repository if you aren't already using Postgres.
**TeslaMate v2 needs a PostgreSQL 16.7 or 17.3 or later database.**
I recommend using the [PostgreSQL 17 add-on][alexbelgium-postgres] from @alexbelgium's repository, or [TimescaleDB][timescaledb] (for advanced users), if you aren't already using PostgreSQL.
To get the full experience, it is recommended that you also install the community [Grafana add-on][grafana-addon] and [MQTT integration][mqtt].
@@ -46,6 +46,13 @@ To get the full experience, it is recommended that you also install the communit
1. Check the logs of the add-on to see if everything went well.
1. Click the `OPEN WEB UI` button to open TeslaMate.
## Upgrading this add-on from v1 to v2
TeslaMate v2 introduced a hard requirement on the version of PostgreSQL database.
**You will need to use PostgreSQL 16.7 or 17.3 or later.**
If you are currently using an earlier version of PostgreSQL, you will need to backup your TeslaMate data, upgrade the database, and then restore the data.
You can find details on how to do this in the "_Migrate TeslaMate to a Different Postres Server_" section of this add-on's documentation tab or in the [DOCS.md][docs-md] file in the add-on repository.
## Migrating to this version of the add-on
Migrating to this version of the add-on should not result in any loss of data, but you can never be too careful, so I recommend you take a full backup of your Home Assistant instance and also a direct backup of the TeslaMate database as detailed in the [TeslaMate documentation][teslamate-backup] before proceeding.
@@ -67,9 +74,10 @@ Everything should pick up where it was before.
[addon]: https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Flildude%2Fha-addons
[addons-repo]: https://github.com/lildude/ha-addons
[alexbegium-postgres]: https://github.com/alexbelgium/hassio-addons/tree/master/postgres
[alexbelgium-postgres]: https://github.com/alexbelgium/hassio-addons/tree/master/postgres_17
[archs]: https://img.shields.io/badge/dynamic/json?color=green&label=Arch&query=%24.arch&url=https%3A%2F%2Fraw.githubusercontent.com%2Flildude%2Fha-addon-teslamate%2Fmain%2Fconfig.json
[docker]: https://hub.docker.com/r/teslamate/teslamate
[docs-md]: https://github.com/lildude/ha-addon-teslamate/blob/main/DOCS.md#migrate-teslamate-to-a-different-postgresql-server
[elixir]: https://elixir-lang.org/
[grafana-addon]: https://github.com/hassio-addons/addon-grafana
[ingres-badge]: https://img.shields.io/badge/dynamic/json?label=Ingress&query=%24.ingress&url=https%3A%2F%2Fraw.githubusercontent.com%2Flildude%2Fha-addon-teslamate%2Fmain%2Fconfig.json
@@ -77,5 +85,6 @@ Everything should pick up where it was before.
[repo-btn]: https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg
[sponsor-badge]: https://img.shields.io/badge/Sponsor_Me-%E2%9D%A4-ec6cb9?logo=GitHub
[teslamate-backup]: https://docs.teslamate.org/docs/maintenance/backup_restore
[teslamate-version]: https://img.shields.io/badge/dynamic/json?label=TeslaMate%20Version&url=https%3A%2F%2Fraw.githubusercontent.com%2Flildude%2Fha-addon-teslamate%2Fmain%2Fbuild.json&query=%24.args.teslamate_version
[teslamate-version]: https://img.shields.io/badge/dynamic/regex?url=https%3A%2F%2Fgithub.com%2Flildude%2Fha-addon-teslamate%2Fblob%2Fmain%2FDockerfile&search=teslamate_version%3D(%3F%3Ccurrent_ver%3E%5Cd%2B%5C.%5Cd%2B%5C.%5Cd%2B)&replace=%241&label=TeslaMate%20Version
[teslamate]: https://github.com/teslamate-org/teslamate/
[timescaledb]: https://github.com/expaso/hassos-addon-timescaledb
-13
View File
@@ -1,13 +0,0 @@
{
"build_from": {
"aarch64": "teslamate/teslamate:not-used",
"amd64": "teslamate/teslamate:not-used",
"armv7": "teslamate/teslamate:not-used"
},
"args": {
"teslamate_version": "1.30.1"
},
"codenotary": {
"signer": "colin@symr.io"
}
}
+81 -69
View File
@@ -1,71 +1,83 @@
{
"name": "TeslaMate",
"version": "dev",
"slug": "teslamate",
"description": "A self-hosted data logger for your Tesla.",
"url": "https://github.com/lildude/ha-addon-teslamate",
"arch": [
"armv7",
"aarch64",
"amd64"
],
"ingress": true,
"init": false,
"panel_icon": "mdi:car-connected",
"panel_title": "TeslaMate",
"options": {
"database_user": null,
"database_pass": null,
"database_name": "teslamate",
"database_host": null,
"database_port": 5432,
"database_ssl": false,
"grafana_import_dashboards": true,
"grafana_folder_name": "TeslaMate",
"grafana_host": "a0d7b954-grafana",
"grafana_port": 3000,
"grafana_user": "",
"grafana_pass": "",
"disable_mqtt": false,
"mqtt_host": "core-mosquitto",
"mqtt_user": "",
"mqtt_pass": "",
"mqtt_tls": false,
"mqtt_tls_accept_invalid_certs": false,
"mqtt_namespace": "",
"timezone": "Europe/London",
"import_dir": "/share/teslamate",
"default_geofence": "",
"encryption_key": ""
},
"schema": {
"database_user": "str",
"database_pass": "password",
"database_name": "str",
"database_host": "str",
"database_ssl": "bool",
"database_port": "int",
"grafana_import_dashboards": "bool",
"grafana_folder_name": "str",
"grafana_host": "str",
"grafana_port": "int",
"grafana_user": "str?",
"grafana_pass": "password?",
"disable_mqtt": "bool",
"mqtt_host": "str?",
"mqtt_user": "str?",
"mqtt_pass": "password?",
"mqtt_tls": "bool",
"mqtt_tls_accept_invalid_certs": "bool",
"mqtt_namespace": "str?",
"timezone": "str",
"import_dir": "str",
"default_geofence": "str?",
"encryption_key": "password?"
},
"map": [
"share:rw"
],
"codenotary": "colin@symr.io",
"image": "ghcr.io/lildude/ha-addon-teslamate-{arch}"
"name": "TeslaMate",
"version": "dev",
"slug": "teslamate",
"description": "A self-hosted data logger for your Tesla.",
"url": "https://github.com/lildude/ha-addon-teslamate",
"arch": [
"aarch64",
"amd64"
],
"hassio_api": true,
"ingress": true,
"init": false,
"panel_icon": "mdi:car-connected",
"panel_title": "TeslaMate",
"ports": {
"4000/tcp": null
},
"ports_description": {
"4000/tcp": "Web interface (not required for Home Assistant ingress)"
},
"options": {
"database_user": null,
"database_pass": null,
"database_name": "teslamate",
"database_host": null,
"database_port": 5432,
"database_ssl": false,
"grafana_import_dashboards": true,
"grafana_folder_name": "TeslaMate",
"grafana_host": "a0d7b954-grafana",
"grafana_port": 3000,
"grafana_user": "",
"grafana_pass": "",
"disable_mqtt": false,
"mqtt_host": "core-mosquitto",
"mqtt_user": "",
"mqtt_pass": "",
"mqtt_tls": false,
"mqtt_tls_accept_invalid_certs": false,
"mqtt_namespace": "",
"timezone": "Europe/London",
"import_dir": "/share/teslamate",
"default_geofence": "",
"encryption_key": "",
"env_vars": []
},
"schema": {
"database_user": "str",
"database_pass": "password",
"database_name": "str",
"database_host": "str",
"database_ssl": "bool",
"database_port": "int",
"grafana_import_dashboards": "bool",
"grafana_folder_name": "str",
"grafana_host": "str",
"grafana_port": "int",
"grafana_user": "str?",
"grafana_pass": "password?",
"disable_mqtt": "bool",
"mqtt_host": "str?",
"mqtt_user": "str?",
"mqtt_pass": "password?",
"mqtt_tls": "bool",
"mqtt_tls_accept_invalid_certs": "bool",
"mqtt_namespace": "str?",
"timezone": "str",
"import_dir": "str",
"default_geofence": "str?",
"encryption_key": "password?",
"env_vars": [
{
"name": "str?",
"value": "str?"
}
]
},
"map": [
"share:rw"
],
"image": "ghcr.io/lildude/ha-addon-teslamate-{arch}"
}
+38 -15
View File
@@ -11,13 +11,14 @@
set -o errexit
readonly HOST=$(bashio::config 'grafana_host')
readonly PORT=$(bashio::config 'grafana_port')
readonly USER=$(bashio::config 'grafana_user')
readonly PASS=$(bashio::config 'grafana_pass')
readonly DASHBOARDS_DIRECTORY="/dashboards"
readonly FOLDER_NAME=$(bashio::config 'grafana_folder_name')
HOST=$(bashio::config 'grafana_host')
PORT=$(bashio::config 'grafana_port')
USER=$(bashio::config 'grafana_user')
PASS=$(bashio::config 'grafana_pass')
DASHBOARDS_DIRECTORY="/dashboards"
FOLDER_NAME=$(bashio::config 'grafana_folder_name')
readonly HOST PORT USER PASS DASHBOARDS_DIRECTORY FOLDER_NAME
main() {
@@ -31,7 +32,6 @@ main() {
restore) restore;;
*) exit 1;;
esac
}
@@ -42,9 +42,7 @@ backup() {
dashboard_json=$(get_dashboard "$dashboard")
if [[ -z "$dashboard_json" ]]; then
echo "ERROR:
Couldn't retrieve dashboard $dashboard.
"
echo "ERROR: Couldn't retrieve dashboard $dashboard."
exit 1
fi
@@ -56,10 +54,36 @@ backup() {
restore() {
bashio::log.info "Checking for Grafana folder: $FOLDER_NAME"
FLD=$(curl --silent --show-error \
bashio::log.info "Checking for Grafana datasource: TeslaMate"
datasources=$(curl --silent --show-error \
--user "$LOGIN" -H "Content-Type: application/json" \
$URL/api/folders | jq ".[] | select(.title==\"$FOLDER_NAME\")")
"$URL/api/datasources")
if [[ $datasources == *"statusCode"* ]]; then
bashio::log.error "Error getting Grafana datasources: $(echo "$datasources" | jq -r .message)"
bashio::log.debug "$datasources"
exit 1
fi
DS=$(echo "$datasources" | jq ".[] | select(.name==\"TeslaMate\")")
if [[ -z "$DS" ]]; then
bashio::log.error "'TeslaMate' datasource not found. Please create or rename your datasource. It must be named 'TeslaMate'."
exit 1
fi
bashio::log.info "Checking for Grafana folder: $FOLDER_NAME"
folders=$(curl --silent --show-error \
--user "$LOGIN" -H "Content-Type: application/json" \
"$URL/api/folders")
if [[ $folders == *"statusCode"* ]]; then
bashio::log.error "Error getting Grafana folders: $(echo "$folders" | jq -r .message)"
bashio::log.debug "$folders"
exit 1
fi
FLD=$(echo "$folders" | jq ".[] | select(.title==\"$FOLDER_NAME\")")
if [[ -z "$FLD" ]]; then
bashio::log.info "Not found... creating"
@@ -72,7 +96,7 @@ restore() {
"$URL/api/folders")
fi
FOLDER_ID=$(echo $FLD | jq -r .id)
FOLDER_ID=$(echo "$FLD" | jq -r .id)
if [[ -z "$FOLDER_ID" ]]; then
bashio::log.error "Could not determine Grafana folder id"
@@ -123,5 +147,4 @@ list_dashboards() {
cut -d '/' -f2
}
main "$@"
View File
+7 -1
View File
@@ -1,5 +1,11 @@
#!/command/with-contenv bashio
# -*- bash -*-
# shellcheck shell=bash
mkdir /run/nginx
bashio::log.info "Starting NGINX..."
if [ -f /run/nginx ]; then
rm -f /run/nginx
fi
mkdir -p /run/nginx
exec nginx -g 'daemon off;error_log /proc/1/fd/1 error;'
+1
View File
@@ -0,0 +1 @@
longrun
+125
View File
@@ -0,0 +1,125 @@
#!/usr/bin/with-contenv bashio
# -*- bash -*-
# shellcheck shell=bash
set -e
# Database things
DATABASE_HOST=$(bashio::config 'database_host')
DATABASE_NAME=$(bashio::config 'database_name')
DATABASE_PASS=$(bashio::config 'database_pass')
DATABASE_PORT=$(bashio::config 'database_port')
DATABASE_USER=$(bashio::config 'database_user')
# MQTT things
DISABLE_MQTT=$(bashio::config 'disable_mqtt')
MQTT_HOST=$(bashio::config 'mqtt_host')
MQTT_NAMESPACE=$(bashio::config 'mqtt_namespace')
MQTT_PASSWORD=$(bashio::config 'mqtt_pass')
MQTT_TLS_ACCEPT_INVALID_CERTS=$(bashio::config 'mqtt_tls_accept_invalid_certs')
MQTT_TLS=$(bashio::config 'mqtt_tls')
MQTT_USERNAME=$(bashio::config 'mqtt_user')
# Other things
IMPORT_DIR=$(bashio::config 'import_dir')
PORT=4000
TZ=$(bashio::config 'timezone')
DEFAULT_GEOFENCE=$(bashio::config 'default_geofence')
encryption_key=$(bashio::config 'encryption_key')
if bashio::config.is_empty 'encryption_key'; then
encryption_key=$(openssl rand -hex 32)
bashio::addon.option 'encryption_key' "$encryption_key"
fi
ENCRYPTION_KEY=$(bashio::config 'encryption_key')
ingress_entry=$(curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
-H "Content-Type: application/json" \
-s http://supervisor/addons/self/info | \
jq -r '.data.ingress_entry')
export URL_PATH=$ingress_entry
export DATABASE_HOST DATABASE_NAME DATABASE_PASS DATABASE_PORT DATABASE_USER \
DISABLE_MQTT MQTT_HOST MQTT_NAMESPACE MQTT_PASSWORD MQTT_TLS_ACCEPT_INVALID_CERTS \
MQTT_TLS MQTT_USERNAME IMPORT_DIR PORT TZ DEFAULT_GEOFENCE ENCRYPTION_KEY URL_PATH
# These are optional and applied last so we can override those set above if needed.
for env_var in $(bashio::config 'env_vars|keys'); do
name=$(bashio::config "env_vars[${env_var}].name")
value=$(bashio::config "env_vars[${env_var}].value")
bashio::log.debug "Setting Env Variable ${name} to ${value}"
export "${name}=${value}"
done
# Create the PostgreSQL database if it doesn't exist
bashio::log.info "Checking for database '$DATABASE_NAME' on $DATABASE_HOST"
if pg_isready -h "$DATABASE_HOST" -p "$DATABASE_PORT" > /dev/null 2>&1; then
# Check the version of PostgreSQL database is v16.7 or later or v17.3 or later
pg_version=$(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -Atqc "SELECT regexp_replace(version(), 'PostgreSQL ([^ ]+) .*', '\\1') AS version")
major_version=$(echo "$pg_version" | cut -d '.' -f 1)
minor_version=$(echo "$pg_version" | cut -d '.' -f 2)
compat=true
case "$major_version" in
16)
[[ "$minor_version" -lt 7 ]] && compat=false
;;
17)
[[ "$minor_version" -lt 3 ]] && compat=false
;;
*)
[[ "$major_version" -lt 16 ]] && compat=false
;;
esac
if [ $compat = false ]; then
bashio::log.error "PostgreSQL version $pg_version is not supported. Please upgrade to v16.7 or later or v17.3 or later."
exit 1
fi
if [[ -n $(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -Atqc "SELECT datname FROM pg_database WHERE datname = '$DATABASE_NAME'") ]]; then
bashio::log.info "Database $DATABASE_NAME already exists"
else
if PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -qc "CREATE DATABASE \"$DATABASE_NAME\""; then
bashio::log.info "Database $DATABASE_NAME created"
else
bashio::log.error "Failed to create database $DATABASE_NAME"
exit 1
fi
fi
else
bashio::log.error "PostgreSQL at '$DATABASE_HOST' is not ready or unreachable"
exit 1
fi
# Import dashboards
if bashio::config.true 'grafana_import_dashboards'; then
bashio::log.info "Importing Grafana dashboards..."
/dashboards.sh restore || bashio::log.error "Failed to import dashboards"
fi
if [[ -z $(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" "$DATABASE_NAME" -Atqc "SELECT 1 FROM settings WHERE base_url LIKE '%api/hassio_ingress%'" 2> /dev/null ) ]]; then
teslamate_ingress=$(bashio::addon.ingress_entry)
grafana_ingress=$(bashio::addon.ingress_entry "$(bashio::supervisor.addons | grep grafana)")
if [[ "$teslamate_ingress" == "$grafana_ingress" ]]; then
grafana_ingress="** Grafana addon not found. Ensure the addon is installed and running. **"
fi
bashio::log.notice ""
bashio::log.notice "Configure TeslaMate settings by adding these values"
bashio::log.notice "to the URL you use to access your Home Assistant instance:"
bashio::log.notice ""
bashio::log.notice "=> Web App: $teslamate_ingress"
bashio::log.notice "=> Dashboards: $grafana_ingress"
bashio::log.notice ""
fi
# Set max open file limit to avoid memory allocation issues
if [ "$(ulimit -Hn)" -gt 524288 ]; then
ulimit -n 524288
fi
bashio::log.info "Starting TeslaMate..."
cd /opt/app
exec $(/usr/bin/env sh) /entrypoint.sh bin/teslamate start
@@ -0,0 +1 @@
longrun
-76
View File
@@ -1,76 +0,0 @@
#!/usr/bin/with-contenv bashio
# -*- bash -*-
# shellcheck shell=bash
set -e
# Database things
DATABASE_HOST=$(bashio::config 'database_host')
DATABASE_NAME=$(bashio::config 'database_name')
DATABASE_PASS=$(bashio::config 'database_pass')
DATABASE_PORT=$(bashio::config 'database_port')
DATABASE_USER=$(bashio::config 'database_user')
# MQTT things
DISABLE_MQTT=$(bashio::config 'disable_mqtt')
MQTT_HOST=$(bashio::config 'mqtt_host')
MQTT_NAMESPACE=$(bashio::config 'mqtt_namespace')
MQTT_PASSWORD=$(bashio::config 'mqtt_pass')
MQTT_TLS_ACCEPT_INVALID_CERTS=$(bashio::config 'mqtt_tls_accept_invalid_certs')
MQTT_TLS=$(bashio::config 'mqtt_tls')
MQTT_USERNAME=$(bashio::config 'mqtt_user')
# Other things
IMPORT_DIR=$(bashio::config 'import_dir')
PORT=4000
TZ=$(bashio::config 'timezone')
DEFAULT_GEOFENCE=$(bashio::config 'default_geofence')
encryption_key=$(bashio::config 'encryption_key')
if bashio::config.is_empty 'encryption_key'; then
encryption_key=$(openssl rand -hex 32)
bashio::addon.option 'encryption_key' "$encryption_key"
fi
ENCRYPTION_KEY=$(bashio::config 'encryption_key')
ingress_entry=$(curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
-H "Content-Type: application/json" \
-s http://supervisor/addons/self/info | \
jq -r '.data.ingress_entry')
export URL_PATH=$ingress_entry
export DATABASE_HOST DATABASE_NAME DATABASE_PASS DATABASE_PORT DATABASE_USER \
DISABLE_MQTT MQTT_HOST MQTT_NAMESPACE MQTT_PASSWORD MQTT_TLS_ACCEPT_INVALID_CERTS \
MQTT_TLS MQTT_USERNAME IMPORT_DIR PORT TZ DEFAULT_GEOFENCE ENCRYPTION_KEY URL_PATH
# Import dashboards
if bashio::config.true 'grafana_import_dashboards'; then
/dashboards.sh restore
fi
# Create the PostgreSQL database if it doesn't exist
if pg_isready -h "$DATABASE_HOST" -p "$DATABASE_PORT" > /dev/null 2>&1; then
bashio::log.info "Creating database '$DATABASE_NAME' on $DATABASE_HOST"
if [[ -n $(PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -Atqc "\list $DATABASE_NAME") ]]; then
bashio::log.info "Database $DATABASE_NAME already exists"
else
if PGPASSWORD="$DATABASE_PASS" psql -h "$DATABASE_HOST" -p "$DATABASE_PORT" -U "$DATABASE_USER" postgres -qc "CREATE DATABASE \"$DATABASE_NAME\""; then
bashio::log.info "Database $DATABASE_NAME created"
else
bashio::log.error "Failed to create database $DATABASE_NAME"
exit 1
fi
fi
else
bashio::log.error "PostgreSQL at '$DATABASE_HOST' is not ready or unreachable"
exit 1
fi
ulimit -n 1048576
bashio::log.info "Starting TeslaMate"
cd /opt/app
exec $(/usr/bin/env sh) /entrypoint.sh bin/teslamate start
+3
View File
@@ -67,3 +67,6 @@ configuration:
import_dir:
name: TeslaFi Import Directory
description: The directory which holds the TeslaFi export you would like to import into TeslaMate.
env_vars:
name: Environment Variables
description: Set additional environment variables for TeslaMate which aren't currently exposed by the configuration options in this add-on.